于2017-03-26由小牛君创建
1、 内联函数和宏定义
宏定义是C语言提出的,它在预处理的地方将代码展开,这样做,相比于函数调用,可减少额外的时间和空间开销。
但是宏定义有一些缺点,最明显的是,它容易产生二义性。如:#define MAX(a, b) a>b?a:b
使用该宏: MAX( num1, num2 ,因为宏展开后变成 num1>num2?num1:num2,但是,如果是这样调用的:MAX( 17+32, 25+21),编译时出现错误,原因是,宏展开后变成: 17+32>25+21?17+32:25+21(shit!!!)
所以,宏在使用时,参数一定要加上括号,上述的那个例子改成如下所示就能解决问题了。
#define MAX( (a), (b) ) (a)>(b)?(a)b)
即使是这样,这个宏仍有Bug,因为如果我这样调用 MAX(i++,j++),经过这个宏以后, i 和 j 都被累加了两次。
为了克服C语言宏的断言,C++提提出了内联函数。内联函数和宏的区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。 而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销,而且,内联函数会进行参数类型检查,更加安全。
总结:
相同点:(1) 两者都会对定义进行展开,以减少函数调用带来的开销; (2) 两者均适合代码量较小的函数,如果代码量过大,宏展开会占用大量内存,会降低程序执行速度;而对于内联函数,编译器可能会将之视为非内联函数使用。
不同点:宏是由预处理器对宏进行文本替换,而内联函数通过编译器进行替换,且它是真正的函数,会对参数类型进行检查,更加安全。
2、 Const作用
(1) 定义const常量,如:const int MAX=100
(2) 进行类型检查,防止变量被修改。void f(const int i)
(3) 函数重载,void f(int i); void f(int i) const;
(4) 提高效率,保存符号表中,成为编译期间的常量,没有存储和读内存操作。
3、 const使用
(1) 修饰常量
(2) 指针const
char* const pContent;
const char* pConstent;
(3) 函数中的const
参数为const,函数不可修改const参数的值(引用)
返回const,一般用于操作符重载,若修饰普通函数返回的对象,则对象亦为const
(4) 类const
类中的成员函数可以定义为const类型的,表示该函数不会改变类的成员。如:
class A{
void func() const;
};
4、 指针与引用比较
相同点:两者都可以避免数据复制
不同点:(1) 初始化:指针可以为空,而引用不可以,它必须和某个变量绑定。 (2) 安全性:引用在使用前不测试安全性,而指针会。 (3) 可修改性:指针可重新指向一个对象,而引用不可以,在这一点上,引用类似于const指针。
如:
int a = 2; //int &b; wrong!!! int & b =a; int c = 4; b = c; //a=4, b=4
5、 缺省参数函数
C++允许函数列表中的某些参数有缺省值,当函数调用省略了实参时,程序将自动为它赋一个缺省值。
使用缺省参数时,所有的缺省参数必须位于函数参数列表的右边,即,如果某个参数有缺省值,那么它右边的所有参数均要有缺省值。
如:
void eat(Apple &apple, Orange & orange, int apple_num=1, int orange_num=1);
//void eat(Apple &apple, Orange & orange, int apple_num=1, int orange_num); wrong!!!
6、 函数重载
函数重载实际上也是C++中多态的一种,它让用户可使用不同的参数调用同一个函数。函数重载的方法是让相同函数名的函数有不同的参数列表(参数数目或者参数类型,注意,无返回值无关),以便针对不同的参数输入进行类似逻辑的处理。这种机制在C++的类(如:构造函数)和API(如string的API)中经常使用。
有些情况下,函数重载可以被缺省参数函数替代。
7、 函数模板
对不同类型使用一种算法函数时,可使用模板。如:交换a和b(a,b类型相同且可为int,doube或者float),swap(Type a, Type b)。
如:
template <class Any> //or template <typename Any> void Swap(Any &a, Any &b) { Any temp; temp = a; a = b; b=temp; }
有四个概念:非模板函数,模板函数,显式具体化模板函数和显式实例化模板函数。
非模板函数:
Void Swap(job &, job &);
模板函数:
template <class Any>
void Swap(Any &a, Any &b);
显式具体化模板函数:
template <> void Swap<job> (job &, job &);
显式实例化模板函数:编译器能通过隐式实例化,来使用模板生成函数定义,而新版本的C++允许显式实例化。
template void Swap<job> (job &, job &);
编译器在选择原型时,非模板函数优于显示具体化和模板版本,而显式具体化优化模板版本。
编译器选择使用哪个函数版本?从最佳到最差的顺序是:
(1) 完全匹配, 但常规函数优先于模板
(2) 提升转换(如,char和shorts自动转化为int,float自动转化为double)
(3) 标准转换(如,int转化为char,long转化为double)
(4) 用户自定义转换,如类声明中定义的转换。
—————————————————— ———————————————————————————————-
——————————————————- New Chapter —————————————————————————-
—————————————————— ———————————————————————————————-
《C++ Primer plus》学习笔记之“内存模型和名称空间”
本章主要讲了C++存储持续性,作用域,链接性,布局new操作符和名称空间。
1、 关键字static和extern
关键字static修饰函数或者变量时,表明该函数或者变量具有内部链接性,即只有本文件内部可以使用。
关键字extern修饰函数或者变量时,表明该函数或者变量在其他头文件中定义。
2、 布局new操作符
在C++中,经常使用new/delete申请或者释放空间,会导致程序性能非常低下,这时候可以采用布局new操作符,它允许用户将创建的对象放到一个静态缓冲区中。如:
char buffer[1024]; (MyClass *pclass=) new (buffer) MyClass;//put object in buffer //delete pclass; never delete a static buffer!!! Pclass->~MyClass();//explicitly invoke destructor function
实际上,布局new操作符在STL中得到了广泛的使用。
使用new操作符时,需要注意:
(1) 加上头文件#include <new>
(2) 不要delete该对象
(3) 显式调用析构函数
3、 命名空间
为了更好地控制名称的作用域,解决冲突问题,便于多人开发的代码进行集成,C++提供了名称空间。
namespace Name{ // your code here}
4、 malloc与new的区别
(1) malloc/free是C/C++语言的标准库函数,而new/delete是C++操作符,两者均可以申请和释放内存
(2) 对于自定义的对象而言,malloc/free无法满足动态对象的要求,即:无法创建对象时自动调用构造函数,对象消亡之前自动调用析构函数。
总结:malloc是一个内存分配函数,需要头文件支持,分配的是一块内存区域,用指针访问即可;而new是运算符,创建的是个对象,用函数成员访问,不能直接访问地址。
在线咨询
免费热线
资料发放
技术答疑
关注微信