6. 内联函数
在函数前加入inline修饰即可将函数变为内联函数。所谓内联函数,就是在编译时C++编译器会将函数体在调用内联函数的地方展开,从而省去了调用函数的栈帧开销,提高程序运行效率。
inline int Add(int a, int b)
{
return a + b;
}
int main()
{
int c = 0;
c = Add(1, 2);
return 0;
}
对上述代码,在Debug版本下,我们观察汇编代码,发现仍然调用了函数Add而不是我们希望的内联展开形式。
这是因为内联函数展开后就不可调试了,而Debug版本为了方便我们调试,因此忽略了inline的选项。为了可以看到内联函数的变化,我们可以做出如下设定,再在X86环境下编译程序,这样Debug版本也可以实现内联了。或者使用release版本也可以,release会自动对其进行优化,变为内联展开。
对内联函数做以总结:
①内联函数inline实际上是一种以空间换时间的做法,因为编译器在处理内联函数时会使用函数体替换函数调用,这样会使得目标文件变大,但是省去了调用,提高了效率。
②对于inline的使用,一般将规模小、非递归、频繁调用的函数采取inline修饰。而且inline修饰并不是一定会替换调用,只是向编译器表达内联的希望,具体是否内联要取决于编译器,所以复杂又庞大的函数编译器会忽略内联的请求。
③inline不建议声明与定义分离,这可能会导致需要展开函数时找不到函数体导致链接错误。
7. auto关键字
在C语言中变量一般有三种类型:①auto:自动变量(局部变量),一般省略不写;②static:静态区变量;③extern:外部变量。在C++中,对auto做了新的定义,auto是类型指示符,他可以充当类型声明的占位符,根据初始化类型替换为实际类型。
int func()
{
return 9;
}
int main()
{
//auto可以根据初始化表达式推导实际类型
auto a = 1;
auto b = 1.24;
auto c = "hello";
auto d = func();
//typeid(变量).name() 可以打印出符号的类型
cout << typeid(a).name() << endl; //int
cout << typeid(b).name() << endl; //double
cout << typeid(c).name() << endl; //char const* __ptr64
cout << typeid(d).name() << endl; //int
return 0;
}
在使用auto时需要注意以下几点:
①在使用auto来声明指针变量的时候,*号个数应小于等于指针级数,例如对四级指针,auto后的*小于等于4个都是可以的,但是不可以大于4个。
int main()
{
int a = 1;
auto p1 = &a;
auto* p2 = &a;
//auto* p3 = a; //error
auto p3 = &p1;
auto p4 = &p3;
auto*** p5 = &p3;
//auto**** p6 = &p3; //error
cout << typeid(p1).name() << endl; //int * __ptr64
cout << typeid(p2).name() << endl; //int * __ptr64
cout << typeid(p3).name() << endl; //int * __ptr64 * __ptr64
cout << typeid(p4).name() << endl; //int * __ptr64 * __ptr64 * __ptr64
cout << typeid(p5).name() << endl; //int * __ptr64 * __ptr64 * __ptr64
return 0;
}
②在使用auto声明引用类型时,必须要加&。
int main()
{
int a = 1;
auto& ra = a;
cout << typeid(ra).name() << endl; //int
return 0;
}
③当使用auto定义多个变量时,该行变量类型应该相同,因为编译器只会对第一个类型推导,然后以此结论定义剩下的变量。
int main()
{
auto a = 1, b = 8;
//auto c = 'k', d = 1.3; //error
return 0;
}
④auto可以作为函数的返回类型,但不可以作为形参的类型。auto也不可以用来声明数组类型。
auto func1()
{
return 1;
}
//int func2(auto c) error
//{
// return 2;
//}
int main()
{
auto a = func1();
cout << typeid(a).name() << endl; //int
//auto b = func2(a);
//auto arr[3] = {1, 2, 3}; //error
return 0;
}
8.基于范围的for循环
基于范围的for循环括号内冒号前是用于迭代的变量,冒号后是迭代的范围,也可以使用continue和break进行控制。暂时只介绍到数组,循环是从数组第一个元素到最后一个。
int main()
{
int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
//1.一般的for循环
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
cout << arr[i] << ' ';
cout << endl;
//2.基于范围的for循环
for (int num : arr) //类似于python --- for num in arr:
cout << num << ' ';
cout << endl;
return 0;
}
9.空指针nullptr
在C语言我们所使用的空指针NULL实际上是一个宏,它可能被定义为0,同时也可能被定义为((void*)0)这样一个无类型指针。因此引入nullptr,定义它为((void*)0),代替以往NULL的使命。nullptr在C++中作为关键字存在。
void func(int a)
{
cout << "int" << endl;
}
void func(int* a)
{
cout << "int*" << endl;
}
int main()
{
func(0); //int
func(NULL); //int
func((int*)NULL); //int*
func(nullptr); //int*
}