C++整理

名字空间
• 无名名字空间
– 不属于任何有名名字空间的标识符,隶属于无名命
名空间
– 无名命名空间的成员,直接通过“::”访问
• 名字空间嵌套与名字空间别名
– 内层标识符隐藏外层同名标识符
– 嵌套的名字空间需要逐层分解
– 可通过名字空间别名简化书写
namespace ns_four = ns1::ns2::ns3::ns4;

结构、联合和枚举
• C++的结构
– 声明或定义结构型变量,可以省略struct关键字
– 可以定义成员函数,在结构体的成员函数中可以直
接访问该结构体的成员变量,无需通过“.”或“->”
• C++的联合
– 声明或定义联合型变量,可以省略union关键字
– 支持匿名联合
• C++的枚举
– 声明或定义枚举型变量,可以省略enum关键字
– 独立的类型,和整型之间不能隐式相互转换

布尔类型
• 表示布尔量的数据类型
– bool
• 布尔类型的字面值常量
– true表示真
– false表示假
• 布尔类型的本质
– 单字节整数,用1和0表示真和假
• 任何基本类型都可以被隐式转换为布尔类型
– 非0即真,0即假

操作符别名
• 某些欧洲国家语言使用的字母比26个基本拉丁字母多,占用了
ASCII码表及键盘中&等特殊符号的位置,因此规定了一些操作
符的别名,以便使用这些国家的键盘也能正确输入C++源代码
主名  别名  主名  别名  主名  别名
{  <%  &&  and  |  bitor
}  %>  ||  or  |=  or_eq
[  <:  !  not  ^  xor
]  :>  !=  not_eq  ^=  xor_eq
#  %:  &  bitand  ~  compl
##  %:%:  &=  and_eq

重载
• 重载关系
– 同一作用域中,函数名相同,参数表不同的函数
– 只有同一作用域中的同名函数才涉及重载问题,不
同作用域中同名函数遵循标识符隐藏原则
• 重载解析
– 完全匹配>常量转换>升级转换>标准转换>自定义
转换>省略号匹配
• 函数指针的类型决定其匹配的重载版本

重载
• 重载是通过C++换名实现的
• 通过extern "C"可以要求C++编译器按照C方式处
理函数接口,即不做换名,当然也就无法重载

缺省参数和哑元
• 可以为函数的参数指定缺省值,调用该函数时若未
指定实参,则与该实参相对应的形参取缺省值
• 函数参数的缺省值只能在函数声明中指定
• 如果函数的某一个参数具有缺省值,那么该参数后
面的所有参数必须都具有缺省值
• 不要因为使用缺省参数而导致重载歧义
• 只指定类型而不指定名称的函数参数,谓之哑元
– 保证函数的向下兼容
– 形成函数的重载版本

内联
• 内联就是用函数已被编译好的二进制代码,替换对
该函数的调用指令
• 内联在保证函数特性的同时,避免了函数调用开销
• 注意内联与有参宏(宏函数)的区别

内联
• 内联会使可执行文件的体积和进程代码的内存变大,
因此只有频繁调用的简单函数才适合内联
• 若函数在类声明中直接定义,则自动被优化为内联,
否则可在其声明处加上inline关键字
• inline关键字仅表示期望该函数被优化为内联,但
是否适合内联则完全由编译器决定
• 稀少被调用的复杂函数和递归函数都不适合内联

 • 继续使用标准C库函数malloc/calloc/realloc/free
• 使用new/delete操作符在堆中分配/释放内存
– int* pi = new int;
delete pi;
• 在分配内存的同时初始化
– int* pi = new int (100);
• 以数组方式new的也要以数组方式delete
– int* pi = new int [4] {1, 2};
delete[] pi;

动态内存分配
• 通过new操作符分配N维数组,返回N-1维数组指针
– int (*prow) [4] = new int [3][4];
– int (*ppage) [4][5] = new int [3][4][5];
• 不能通过delete操作符释放已释放过的内存
• delete野指针后果未定义,delete空指针安全
• 内存分配失败,new操作符抛出bad_alloc异常
• 定位分配
– new (指针) 类型 (初值);
– 在一个已分配的内存空间中创建对象

引用
• 引用即别名
– int a = 10;
int& b = a;
int& c = b;
• 引用必须初始化
– int& b; // ERROR !
int& b = a;
const int& b = 10;
• 引用不能为空
• 引用不能更换目标

引用
• 引用型参数,函数的形参是实参的别名
– 在函数中修改实参值
– 避免对象复制的开销
• 常引用型参数
– 接受常量型实参
– 防止对实参的意外修改

引用
• 引用型返回值,从函数中返回引用,一定要保证在
函数返回以后,该引用的目标依然有效
– 返回左值
– 可以返回全局、静态乃至成员变量的引用
– 可以返回在堆中动态创建的对象的引用
– 可以返回调用对象自身的引用
– 可以返回引用型参数本身
– 不能返回局部变量的引用
• 常引用型返回值
– 返回右值

引用
• 在实现层面,引用就是指针,但在语言层面,引用
不是实体类型,因此与指针存在明显的差别
– 指针可以不初始化,其目标可在初始化后随意变更
(除非是指针常量),而引用必须初始化,且一旦初始
化就无法变更其目标
– 存在空指针,不存在空引用
– 存在指向指针的指针,不存在引用引用的引用
– 存在引用指针的引用,不存在指向引用的指针
– 存在指针数组,不存在引用数组,但存在数组引用

显式类型转换
• C风格的显式类型转换
– (目标类型)源类型变量
• C++风格的显式类型转换
– 目标类型(源类型变量)
• 静态类型转换
– static_cast<目标类型> (源类型变量)
– 隐式类型转换的逆转换
– 自定义类型转换

显式类型转换
• 动态类型转换
– dynamic_cast<目标类型> (源类型变量)
– 多态父子类指针或引用之间的转换
• 常类型转换
– const_cast<目标类型> (源类型变量)
– 去除指针或引用上的const属性
• 重解释类型转换
– reinterpret_cast<目标类型> (源类型变量)
– 任意类型的指针或引用之间的转换
– 任意类型的指针和整型之间的转换

来自C++社区的建议
• 用const取代常量宏,用enum取代唯一标识宏,
用inline取代参数宏,用namespace取代条件编译
解决名字冲突
• 变量随用随声明,并立即初始化
• 少用malloc/free,new/delete更好
• 避免使用void*、指针算术、联合和强制类型转换
• 用string和STL容器取代低级数组
• 树立面向对象的编程思想,程序设计的过程是用类
描述世界的过程,而非用函数处理数据的过程
类和对象

全程目标
• 面向对象
• 类和对象
• 类的定义与实例化
• 构造函数与初始化表
• this指针

• 常函数与常对象
• 析构函数
• 拷贝构造与拷贝赋值
• 静态成员
• 成员指针

面向对象
类和对象
• 拥有相同属性和行为的对象被分成一组,即一个类
• 类可用于表达那些不能直接与内置类型建立自然映
射关系的逻辑抽象
• 类是一种用户自定义的复合数据类型,即包括表达
属性的成员变量,也包括表达行为的成员函数
• 类是现实世界的抽象,对象是类在虚拟世界的实例

类的定义与实例化
• 访问控制限定符
– public
公有成员
谁都可以访问
– protected
保护成员
只有自己和子类可以访问
– private
私有成员
只有自己可以访问
 nb
类的定义与实例化
• 访问控制限定符
– 在C++中,类(class)和结构(struct)已没有本质性的
差别,唯一的不同在于
 类的缺省访问控制属性为私有(private)
 结构的缺省访问控制属性为公有(public)
– 访问控制限定符仅作用于类,而非作用于对象,因
此同一个类的不同对象,可以互相访问非公有部分
– 对不同成员的访问控制属性加以区分,体现了C++
作为面向对象程序设计语言的封装特性

类的定义与实例化
• 构造函数
– 函数名与类名相同,且没有返回类型
– 在创建对象时自动被调用,且仅被调用一次
 对象定义语句
 new操作符
– 为成员变量赋初值,分配资源,设置对象的初始状态
– 对象的创建过程
 为整个对象分配内存空间
 以构造实参调用构造函数
 构造基类部分
 构造成员变量
 执行构造代码

类的定义与实例化
• 类的声明与实现可以分开
class 类名 {
返回类型 函数名 (形参表);
};
返回类型 类名::函数名 (形参表) {
函数体;
}

类的定义与实例化
• 对象的创建与销毁
– 在栈中创建单个对象
类名 对象; // 注意不要加空括号
类名 对象 (实参表);
– 在栈中创建对象数组
类名 对象数组[元素个数];
类名 对象数组[元素个数] = {类名 (实参表), ...};
类名 对象数组[] = {类名 (实参表), ...};

类的定义与实例化
• 对象的创建与销毁
– 在堆中创建/销毁单个对象
类名* 对象指针 = new 类名;
类名* 对象指针 = new 类名 ();
类名* 对象指针 = new 类名 (实参表);
delete 对象指针;
– 在堆中创建/销毁对象数组
类名* 对象数组指针 = new 类名[元素个数];
类名* 对象数组指针 = new 类名[元素个数] {类名 (实参表), ...};
// 上面的写法需要编译器支持C++11标准
delete[] 对象数组指针;

构造函数与初始化表
• 构造函数可以重载
– 构造函数也可以通过参数表的差别化形成重载
– 重载的构造函数通过构造实参的类型选择匹配
– 不同的构造函数版本表示不同的对象创建方式
– 使用缺省参数可以减少构造函数重载版本数量
– 某些构造函数具体特殊的意义
 缺省构造函数:按缺省方式构造
 类型转换构造函数:从不同类型的对象构造
 拷贝构造函数:从相同类型的对象构造

构造函数与初始化表
• 缺省构造函数
– 缺省构造函数亦称无参构造函数,但其未必真的没
有任何参数,为一个有参构造函数的每个参数都提
供一个缺省值,同样可以达到无参构造函数的效果
– 如果一个类没有定义任何构造函数,那么编译器会
为其提供一个缺省构造函数
 对基本类型的成员变量,不做初始化
 对类类型的成员变量和基类子对象,调用相应类型的
缺省构造函数初始化
– 对于已经定义至少一个构造函数的类,无论其构造
函数是否带有参数,编译器都不会为其提供缺省构
造函数

构造函数与初始化表
• 缺省构造函数
– 有时必须自己定义缺省构造函数,即使它什么也不
做,尤其是在使用数组或容器的时候,某些基于早
期C++标准的编译器不支持对象数组的初始化语法
– 有时必须为一个类提供缺省构造函数,仅仅因为它
可能作为另一个类的子对象而被缺省构造
– 若子对象不宜缺省构造,则需要为父对象提供缺省
构造函数,并在其中显式地以非缺省方式构造该子
对象

构造函数与初始化表
• 类型转换构造函数
– 在目标类型中,可以接受单个源类型对象实参的构
造函数,支持从源类型到目标类型的隐式类型转换
class 目标类型 {
目标类型 (const 源类型& src) { … }
};
– 通过explicit关键字,可以强制这种通过构造函数实
现的类型转换必须显式地进行
class 目标类型 {
explicit 目标类型 (const 源类型& src) { … }
};

构造函数与初始化表
• 拷贝构造函数
– 形如
class 类名 {
类名 (const 类名& that) { … }
};
的构造函数被称为拷贝构造函数,用于从一个已定
义的对象构造其同类型的副本,即对象克隆
– 如果一个类没有定义拷贝构造函数,那么编译器会
为其提供一个缺省拷贝构造函数
 对基本类型成员变量,按字节复制
 对类类型成员变量和基类子对象,调用相应类型的拷
贝构造函数

构造函数与初始化表
• 拷贝构造函数
– 如果自己定义了拷贝构造函数,编译器将不再提供
缺省拷贝构造函数,这时所有与成员复制有关的操
作,都必须在自定义拷贝构造函数中编写代码完成
– 若缺省拷贝构造函数不能满足要求,则需自己定义
– 拷贝构造的时机
 用已定义对象作为同类型对象的构造实参
 以对象的形式向函数传递参数
 从函数中返回对象
 某些拷贝构造过程会因编译优化而被省略

构造函数与初始化表
• 自定义构造函数和系统定义构造函数
所有系统定义的构造函数,其访问控制属性均为公有(public)
自定义构造函数  系统定义构造函数
缺省构造函数
缺省拷贝构造函数
除拷贝构造函数以外的
任何构造函数
缺省拷贝构造函数
拷贝构造函数  无

构造函数与初始化表
• 初始化表
– 通过在类的构造函数中使用初始化表,指明该类的
成员变量如何被初始化
– 数组和结构型成员变量需要用花括号“{}”初始化
– 类的类类型成员变量和基类子对象,必须在初始化
表中显式初始化,否则将调动相应类型的缺省构造
函数初始化
– 类的常量型和引用型成员变量,必须在初始化表中
显式初始化
– 类的成员变量按其在类中的声明顺序依次被初始化,
而与其在初始化表中的顺序无关
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值