C++复习

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值