register关键字请求“编译器”将局部变量存储于寄存器中
C语言中无法取得register变量地址
在C++中依然支持register关键字
1、C++编译器有自己的优化方式,不使用register也可能做优化
2、C++中可以取得register变量的地址
C++编译器发现程序中需要取register变量的地址时,register对变量的声明变得无效。
早期C语言编译器不会对代码进行优化,因此register变量是一个很好的补充。
C语言的struct定义了一组变量的集合,C编译器并不认为这是一种新的类型
C++中的struct是一个新类型的定义声明
理论上bool只占用一个字节,
如果多个bool变量定义在一起,可能会各占一个bit,这取决于编译器的实现
1)C语言返回变量的值 C++语言是返回变量本身
C语言中的三目运算符返回的是变量值,不能作为左值使用
C++中的三目运算符可直接返回变量本身,因此可以出现在程序的任何地方
2)注意:三目运算符可能返回的值中如果有一个是常量值,则不能作为左值使用
(a < b ? 1 : b )= 30;
3)C语言如何支持类似C++的特性呢?
当左值的条件:要有内存空间;C++编译器帮助程序员取了一个地址而已
// C语言中 const修饰的变量是一个 常变量,本质还是变量,有自己的地址空间
// C++中 const 变量声明的是一个真正的常量,不是变量,所以编译器不会为该常量分配空间
// const 修饰的常量会被放到 符号表 中
const int a = 10;
// 这里对 const 常量取地址,这一步操作会让编译器为该变量分配空间,分配的空间并不会被 a 使用
int p = (int )&a;
// 通过指针改变指向的空间的值,这个空间是编译器为常量分配的空间,但是常量的值并不在这个空间内
// 所以即使通过指针修改了这个空间的值,也不会影响到 a 本身
*p = 5;
// a 的值不变,因为它的值在符号表中,不在程序运行的空间内
printf (“%d, %p\n”, a, *p);
C++中的const常量类似于宏定义
const int c = 5; ≈ #define c 5
C++中的const常量与宏定义不同
const常量是由编译器处理的,提供类型检查和作用域检查
宏定义由预处理器处理,单纯的文本替换
在程序运行过程中const变量只有一个拷贝,而#define 所定义的宏变量却有多个拷贝,所以宏定义在程序运行过程中所消耗的内存要比const变量的大得多;
引用:
void swap1 (int &a, int &b)
{int tmp = a;a = b;b = tmp;
}void swap2 (int *a, int *b)
{int tmp = *a;*a = *b;*b = tmp;
}int main()
{int a = 10;int b = 20;swap2 (&a, &b);swap1 (a, b);printf ("%d, %d\n", a, b);return 0;
}
引用在C++中的内部实现是一个常指针
Type& name çè Type* const name
C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。
从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做出的细节隐藏
当我们使用引用语法的时,我们不去关心编译器引用是怎么做的
当我们分析奇怪的语法现象的时,我们才去考虑c++编译器是怎么做的
若返回栈变量
// 用非引用类型接收函数返回的引用,就等于将函数返回的引用的数据值,复制给了该接收对象
// 效果和返回非引用数据是一样的
若返回栈变量
不能成为其它引用的初始值
不能作为左值使用
若返回静态变量或全局变量
可以成为其他引用的初始值
即可作为右值使用,也可作为左值使用
const & int e 相当于 const int * const e
普通引用 相当于 int *const e1
当使用常量(字面量)对const引用进行初始化时,C++编译器会为常量值分配空间,并将引用名作为这段空间的别名
使用字面量对const引用初始化后,将生成一个只读变量
普通引用在定义必须要初始化,引用是一块空间的别名,如果空间不存在,引用 就没有意义
内联函数
// 内联函数声明时inline关键字必须和函数定义结合在一起,否则编译器会直接忽略内联请求。
inline int func(int a, int b)
{return a < b ? a : b;
}
/*C++编译器可以将一个函数进行内联编译
被C++编译器内联编译的函数叫做内联函数
内联函数在最终生成的代码中是没有定义的
C++编译器直接将函数体插入在函数调用的地方
内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)
*/
//**C++编译器不一定准许函数的内联请求!
/*内联函数是一种特殊的函数,具有普通函数的特征(参数检查,返回类型等)
内联函数是对编译器的一种请求,因此编译器可能拒绝这种请求
内联函数由 编译器处理,直接将编译后的函数体插入调用的地方
宏代码片段 由预处理器处理, 进行简单的文本替换,没有任何编译过程
*/
/*
现代C++编译器能够进行编译优化,因此一些函数即使没有inline声明,也可能被编译器内联编译
另外,一些现代C++编译器提供了扩展语法,能够对函数进行强制内联
如:g++中的__attribute__((always_inline))属性
*/
/*
C++中内联编译的限制:
不能存在任何形式的循环语句
不能存在过多的条件判断语句
函数体不能过于庞大
不能对函数进行取址操作
函数内联声明必须在调用语句之前
*/
/*
编译器对于内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。因此,当函数体的执行开销远大于压栈,跳转和返回所用的开销时,那么内联将无意义
*/
只有参数列表后面部分的参数才可以提供默认参数值
一旦在一个函数调用中开始使用默认参数值,那么这个参数后的所有参数都必须使用默
将所有同名函数作为候选者
尝试寻找可行的候选函数:
1 )精确匹配实参
2) 通过默认参数能够匹配实参
3) 通过默认类型转换匹配实参
匹配失败:
1) 最终寻找到的可行候选函数不唯一,则出现二义性,编译失败。
2) 无法匹配所有候选者,函数未定义,编译失败。
当使用重载函数名对函数指针进行赋值时:
根据重载规则挑选与函数指针参数列表一致的候选者
严格匹配候选者的函数类型与函数指针的函数类型
构造
// 一个函数返回一个对象的时候会创建一个匿名对象,拿返回的那个对象
// 对匿名对象进行初始化,会调用拷贝构造
// 如果没有去接收函数的返回值的话,匿名对象会立马被销毁
// test6_1();
// 如果用一个对象去接收函数的返回值,先用函数返回的对象去初始化
// 匿名对象,调用一次拷贝构造,然后拿新的对象的名字去命名这个匿名对象
// 匿名对象从无名转成有名
// Test6_1 t1 = test6_1();
当对象作为函数参数传递的时候会调用拷贝构造
析构的顺序和构造的顺序相反,先构造的后析构
// 对象初始化列表,在构造函数后面加:,后面加上要初始化的对象
// 对象初始化列表要比当前类的构造函数先执行
// 对象的初始化先后顺序和 在对象初始化列表 的顺序无关,和在类中的声明先后顺序有关
Test9_2():m_a(10), m_c(30), m_b(20), m_ca(100)
{printf ("9_2 222222222222构造函数....\n");
}
// 构造函数中调用构造函数 不会达到预期的效果的
Test10_1(int a, int b)
{m_a = a;m_b = b;Test10_1(a, b, 30); // 匿名对象、临时对象
}
// 静态成员函数只能使用静态成员变量
// 静态成员变量,属于类,不属于某个对象
// 是所有对象共享的,静态是在数据区分配,只有一个备份
// 静态变量不能由某个对象进行初始化
// 静态变量必须在类的外部重新定义并且初始化// 重新定义类的静态变量并且初始化
int Test11::sm_a = 100; // 类的静态变量的使用
// 1、通过某一个对象进行引用
t2.sm_a = 30;// 2、通过类名来引用
Test11::sm_a = 60;
// malloc 和 free:它们本身不是C语言的语法的一部分,是库函数提供的 函数
// new 和 delete: 它们本身是C++语言的一部分,是 运算符 不是 函数
// 创建普通类型变量
int main12_1()
{int *p1 = (int *)malloc(sizeof(int));free(p1);// new + 数据类型int *p2 = new int;*p2 = 10;printf ("*p2 = %d\n", *p2);// 释放 new 出来的空间delete p2;// new 可以在申请空间进行初始化int *p3 = new int(90);printf ("*p3 = %d\n", *p3);delete p3;return 0;
}// 申请数组
int main12_2()
{int *p = (int *)malloc(sizeof(int) * 10);free(p);// 用 new 申请数组 new + 数据类型[size]int *p1 = new int[10];// deleta释放数组 必需要加 [] delete [] p1;return 0;
}class Test12
{
public:Test12(int a, int b){m_a = a;m_b = b;printf ("构造函数\n");}~Test12(){printf ("析构函数\n");}
private:int m_a;int m_b;
};// 动态创建对象
int main12_3()
{Test12 *p = (Test12 *)malloc(sizeof(Test12));free(p);// new 在创建对象的时候会自动构造函数进行对象的构建Test12 *p1 = new Test12(10,20);// delete在进行释放对象的时候会自动调用析构函数进行对象资源的回收delete p1;return 0;
}