再读c++

再论c++

趁着最近一周改论文写论文的烦躁空隙,想到距离上次系统学习已经过去很久了,由于最近一直承担着几个大型的c++开发项目,经常遇到一些难解问题,就决定重新读了一下经典的 thinking in c++ 这本经典书的第一卷,总体感觉这本书对C++的原理类讲解的还是比较清楚的,但是有点缺乏体系。

接下来,从数据抽象、封装、继承、多态几个C++的特性来讲解

一、数据抽象
对象:变量/一块存储区 数据+对数据的操作
封装:数据+函数
抽象数据类型/用户定义类型
允许重声明函数,但不允许重声明结构
头文件中不放任何指令

二、隐藏实现
1、访问控制:private/public/protected
2、友元:显式得不使用当前结构成员函数访问当前结构数据(可以访问私有实现、没有this)
友元函数时可以直接访问类的私有成员或保护成员,它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明。
3、class:默认private struct:默认public
4、隐藏实现
编译好的结构放文件中,而不是头文件
句柄类:只有单指针(struct XXX)和公共接口

三、初始化和清除(安全性)
1、析构函数表示清除
超出作用域自动调用(右括号处被调用),即使用goto退出
2、内存分配
编译器会检查对象定义是否在条件块/goto(跳过构造函数调用的序列点)
3、void指针指向一个对象,则不能正确删除
4、默认构造函数
有构造函数,就会去调用,没有的时候,才会默认

四、函数重载和默认参数
1、名字修饰(编译器内部名字)
(1)全局和成员函数可以重名
(2)重载两个函数名:不同参数类型修饰
2、返回值重载:禁止
3、类型安全连接
函数使用前必须声明(c中编译器会推断函数声明),不会自动添加
4、联合:(union)
不能作为基类且数据在同一内存区覆盖
匿名联合:不需要用标识符/点访问,和访问普通变量一样
5、默认参数
函数声明时就已经给定的一个值
不可以在默认后面跟非默认
不把默认参数作为标志去决定执行函数哪一块
6、占位符参数
函数定义不一定需要标识符

五、常量
1、 头文件里的const
默认内部连接-不创建存储空间,放在符号表 常量折叠
Extern const int bufsize 分配存储空间
2、 安全性
生命周期内不变
3、 指针
const int* u; 一个指针,指向const int
int* const u; 一个const指针,指向int
const int* const u;
4、 赋值和类型检查
非const对象地址const指针
const对象地址 非const指针 (error)
当然要是强制类型转换也可以
5、 传递const
参数不能改变
6、 返回const
返回字面常量:可有可无
返回指针且指向的内容不能被修改
函数返回值引用常量表示不能将函数调用表达式作为左值使用
运算符重载时,如果不想修改使用运算符后的表达式的值,就应该将重载函数返回值设为const int&
7、 临时量
自动成为const

8、 类里的const
建立一个普通的const不给初值,那么构造函数必须给他初始化
9、 内部类型的构造函数
10、 const对象
对象的数据成员在生命周期内不可变
11、 const成员函数
被const对象调用,const对象不能调非const成员函数
构造和析构函数不是const成员函数
12、 volatile
不能被程序员改变,但可以被外部代理程序改变

六、内联函数
在适当的地方像宏一样展开(c++几乎不使用宏),一般放在头文件
类内函数自动成为内联函数
非类内函数必须把函数体和声明结合在一起才有效

七、名字控制(static)
1、 函数内部的静态变量/对象
静态数据区,初始化一次/调用构造函数一次
2、 静态对象的析构函数
main退出或调用exit()时调用
3、 控制连接
外部连接:连接时对连接器可见(全局变量和普通函数),对所有翻译单元可见
内部连接:局部于该单元(static)
extern int a = 0 == int a
static int a;
auto:局部变量
register:局部变量
4、 namespace
可以重复定义,不加分号
别名:namespace bob = bobdakhjsbdas;
未命名:翻译单元内有效
5、 使用名字空间
(1) 作用域运算符 namespace:class:member
(2) using namespace xxx 当前范围内的全局名
(3)

6、 静态成员

7、 嵌套类:可以放静态成员
8、 局部类:不能放静态成员
9、 静态成员函数
可以用普通方法/自我调用,不需要对象
(没有this)只能访问静态数据成员,调用其他静态成员函数

八、引用和拷贝构造函数
1、 c++中的指针

类型更强,显式类型转化
2、 c++中的引用
(1)引用创建必须初始化
(2)不能改变为另一个对象的引用
(3)不能有NULL,必须与合法的存储单元关联
(4)作为函数参数,对引用改变会对函数外改变
(5)作为函数返回值,无论引用关联是什么都要存在

3、常量引用
接受外部变量,不影响不变性

4、指针引用
指针本身增加了
5、拷贝构造函数
从现有的对象创建新对象x(x&)
6、深拷贝和浅拷贝
拷贝者和被拷贝者若是同一个地址,则为浅拷贝,反之为深拷贝。
7、默认拷贝构造函数
类的默认拷贝构造函数只会用被拷贝类的成员的值为拷贝类简单初始化,也就是说二者的p指针指向的内存空间是一致的。

8、防止按值传递
声明一个私有的拷贝构造函数
10、 成员指针

指向类成员的指针语法不能被用来引用类的静态成员静态类成员。是属于该类的全局对象和函数它们的指针是普通指针。

九、运算符重载 operator@
只有包含用户自定义类型才有重载运算符
一元函数定义为成员函数是没有参数,二元函数是出现在符号右侧作为参数

1、 可重载的运算符
不使用c中无意义的、不改变参数个数、不改变优先级
operator=只允许定义在成员函数
operator./operator.*不可重载
2、 自增和自减
++a  operater++()
a++  operater++(int)
3、 代码检测自赋值
释放自身

4、 非常量对象调用常量成员函数
常量对象调用常量成员函数
常量对象不能调用非常量成员函数
5、 返回值优化(RVO)
可以省略函数返回过程中复制构造函数的多余调用
返回临时对象:只是返回,编译器直接创建在外部返回值的内存单元,此时不需要拷贝构造函数,也不需要析构
6、 operator ->
灵巧指针
7、 赋值符

8、 自动创建operator
9、构造函数转化-自动类型转化,构造函数被悄悄调用

explicit阻止构造函数转化
9、 运算符转化

十、继承和组合
1、组合:新类中创建已存在的类
2、继承:创建新类,采取已存在类的形式,把代码加入其中
调用父类:X::print()
1、 构造函数的初始化函数表
显示构造函数调用
Y(int ii):X(ii), i(ii+1) {}
2、 自动析构函数调用:从派生类到基类每一层都调用
3、 名字隐藏
重定义:和基类一样,对普通成员函数重定义
重写:如果基类的成员函数是虚函数
新类如果定义了一个基类的重载函数,那么其他所有版本被隐藏
4、 非自动继承的函数
构造函数、析构函数、operater=
5、 静态成员函数
静态成员函数与普通成员函数的根本区别在于:普通成员函数有 this 指针,可以访问类中的任意成员;而静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)
6、 私有继承
基类所有public变为private
如果希望有一个是public:
public:
using Pet::eat;
using Pet::sleep;
7、 protected继承
允许派生类使用
10、向上类型转换
派生类到基类的转化

operator new返回值必须是void*。第一个参数必须是size_t,还可加其它参数,下文讨论。
operator new重载可以放在全局中,也可以放到类内部。当编译器发现有new关键字,就会现在类和其基类中寻找operator new,找不到就在全局中找,再找不到就用默认的。
在类中的operator new默认就是static。所以加static可以,不加也是全局,可以正常使用。

十一、多态性
1、 定义
接口和具体实现之间的隔离
虚函数
2、 upcasting
派生类到基类的转化
3、 捆绑:函数体和函数调用
早捆绑:程序运行之前
晚捆绑:运行时捆绑
4、 虚函数:virtual
基类virtual—>派生类virtual
重写:派生类对virtual重定义
5、 如何实现晚捆绑
为每个类设置VTABLE、初始化VPTR、为虚函数调用插入代码
6、 VTABLE
特定类的虚函数地址
7、 VPTR:vpointer
指向VTABLE
8、 抽象基类:pure virtual function/abstract
抽象类:纯虚函数
纯抽象类:类中全部是纯虚函数 virtual void func() = 0;
9、 对象切片
向上类型转换时传值
拷贝新的对象去掉了原来对象的一部分
10、重载和重新定义
不允许改变重新定义函数(虚函数)的返回值
重新定义重载成员函数,那么其他被隐藏
设置VPTR是构造函数的工作
实际上,派生类的析构函数会自动调用基类的析构函数。
只要基类的析构函数是虚函数,那么派生类的析构函数不论是否用virtual关键字声明,都自动成为虚析构函数。
一般来说,一个类如果定义了虚函数,则最好将析构函数也定义成虚函数。
析构函数可以是虚函数,但是构造函数不能是虚函数。
析构函数中只有成员函数本地版本被调用,忽略虚机制

11、构造函数和析构函数
在构造函数中调用虚函数,调用的是正在构造的类中的虚函数,而不是子类的虚函数。
纯虚析构函数必须有函数体
单根继承/基于对象的继承:强制容器内所有对象都是从一个基类继承来的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值