文章目录
名字修饰:
C语言中的函数名字修饰规则十分简单,简单到只是在名字前面添加了下划线,所以就没办法支持函数重载,因为在编译的时候函数名会冲突
具体报错:errorLNK2019:无法解析的外部符号_XXX(函数名),该符号在_main中被引用
在C++中,VS编译器下,H指的是int,N指的是double
int Add(int left,int right);
double Add(double left,double right);
int main()
{
Add(1,2);
Add(1.0,2.0);
return 0;
}
对上述代码编译链接会产生如下错误:
errorLNK2019:无法解析的外部符号"double cdecl Add(double,double)"(?Add@@YANNN@Z)
errorLNK2019:无法解析的外部符号"int__cdecl Add(int,int)"(?Add@@YAHHH@Z)
VS中C++把函数返回值写入修饰名,其他编译器不一定
extern “c”:
有些时候C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern “c”,意思是告诉编译器将该函数按照C语言规则来编译
引用:
引用不是新定义一个变量,只是给已存在的变量取了一个别名,两者代表的是同一个,概念上和引用的变量共用一块内存空间
类型& 引用变量名=引用实体;
void TestRef()
{
int a=10;
int& ra=a;
printf("%p\n",&a);
printf("%p\n",&ra);
得到的结果是两者的地址是一模一样的
引用的类型必须和实体是同种类型!
引用在定义时必须进行初始化!
常引用:
const int& b=10;
引用空间名字和地址未知----->临时空间具有常性
引用的使用场景:
1.做参数
void swap(int& left, int& right)
{
int tmp = left;
left = right;
right = tmp;
}
2.做返回值
返回值的生命周期必须不受函数控制(返回值应该用全局变量/static类型变量接收)
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int& ret = Add(1, 2);
Add(3, 4);
cout << ret << endl;
system("pause");
return 0;
}
Add(1,2)调用结束,栈上的空间还在,数据被下一次的Add调用覆盖掉,返回的ret变成了7
3.结构体成员访问设置别名
Struct A
{
int age;
int number;
};
int main()
{
A aa;
aa.age=10;
int& b=aa,age;
b=20;
}
传值和传引用的效率比较:
#include <time.h>
struct A
{
int a[10000];
};
void TestFunc1(A a)
{}
void TestFunc2(A& a)
{}
void TestRefAndValue()
{
A a;
// 以值作为函数参数
size_t begin1 = clock();
for (size_t i = 0; i < 10000; ++i)
{
TestFunc1(a);
}
size_t end1 = clock();
// 以引用作为函数参数
size_t begin2 = clock();
for (size_t i = 0; i < 10000; ++i)
{
TestFunc2(a);
}
size_t end2 = clock();
// 分别计算两个函数运行结束后的时间
cout << "TestFunc1(int*)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(int&)-time:" << end2 - begin2 << endl;
}
// 运行多次,检测值和引用在传参方面的效率区别
int main()
{
for (int i = 0; i < 10; ++i)
{
TestRefAndValue();
}
system("pause");
return 0;
}
传值的效率要比传引用差很多
传指针的效率几乎和传引用一致
引用和指针的区别:
在概念上,引用是实体的一个别名,是没有独立的内存空间的,但是底层实现上实际是有空间的,因为引用是按照指针方式来实现的,空间存放的实际是引用的实体的地址
引用和指针的不同点:
<1>引用在定义的时候必须初始化,指针没有要求
<2>引用在初始化一个实体之后不可以引用其他实体,但是指针可以在任何时候指向一个同类型的实体
<3>没有NULL引用但是有NULL指针
<4>sizeof中引用的大小为实体类型的大小,而指针的话在32位平台下都是4个字节
<5>引用自增代表实体自增,但是指针自增代表指针向后偏移一个类型的大小
(连续空间的指针自增才有意义)
<6>有多级指针,没有多级引用
<7>指针需要显示解引用,但是引用编译器会自己处理
T&<==>T* const
const T&<==>const T* const
内联函数:
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,提升程序运行的效率
release版本会展开,debug版本需要设置才可以展开
如果内联函数中有循环/递归那么编译器优化时会忽略掉内联
inline只能在当前文件中应用
宏的优缺点和替换方法:
优点:
<1>需要改动时,一改全改
<2>提高性能
缺点:
<1>3.14写成’3.14’时报错不明显
<2>max宏函数多次替换,如果内部有自增运算会出现多次自增
C++中的const int =10;相当于宏替换
auto关键字:
使用auto定义变量时必须进行初始化,否则无法根据初始化表达式进行推演变量类型
用auto声明指针类型时,用auto和auto*没有区别,但是auto声明引用类型时必须带&
void TestAuto()
{
auto a=1,b=2;
auto c=3,d=4.0;
}
这段代码编译会失败,因为c和d的初始化表达式类型不同
注意:
<1>auto不能作为函数的参数
<2>auto不能直接用来声明数组
范围for的语法:
对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误.因此C++11中 引入了基于范围的for循环.for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量, 第二部分则表示被迭代的范围
使用条件:
for循环迭代的范围必须是确定的
迭代的对象要实现++和==的操作
指针空值:
NULL实际是一个宏,#define NULL 0
void f(int)
{
cout<<"f(int)"<<endl;
}
void f(int*)
{
cout<<"f(int*)"<<endl;
}
int main()
{
f(0);
f(NULL);
f((int*)NULL);
return 0;
}
这个时候就遇到了麻烦
所以避免混淆,C++11提供了nullptr;代表一个指针空值常量
在使用nullptr表示指针空值时,不需要包含头文件