知识点总结
数据存放位置
- 本地变量:栈
- new出的对象:堆
- static/全局变量:全局数据区
c++内存模型
- 存放对象的地方
- 栈
- 堆
- 全局数据区
- 访问对象的方式
- 变量访问对象
- 指针访问对象
- 引用访问对象
class
- struct默认public
- class默认private
- 其他用法完全一样,都包含成员函数、继承、多态
const
- 只需看其右侧最近的类型
- *前后
- 前:指针指向变量
- 后:指针本身是变量
- 函数前后
- 前:函数返回值为const
- 后:函数成员为const
- 最后:此成员函数不改变类中的成员变量(mutable可变的)
- *前后
static
- 静态局部变量:
- 全局数据区分配内存
- 随函数第一次调用时初始化,程序结束时销毁
- 声明处初始化,若无显式初始化,会自动初始化为0
- 作用域为局部作用域
- 静态全局变量:
- 静态函数:
- 本文件可见(文件隔离)
- 静态数据成员:
- 只有class有一份,而普通数据成员是每一个实例有一份
- 静态成员函数:
- 静态成员函数不能访问非静态(成员函数、数据成员)
- 非静态成员函数可以访问静态(成员函数、数据成员)
- 解释:静态是属于类的,它不知道创建了多少对象;而对象中的数据对类中数据一清二楚
&
- 引用
- 在赋值=右侧
- 和变量在一起
- 取地址
- 在赋值=左侧
- 和类型在一起
初始化
- A::A(int m, int n) : x(m), y(n) {} //初始化
- A::A(int m, int n){x = m; y = n;} //赋值
- singleton模式:local static对象替换non-local static对象
- 基础在于:c++保证,函数内的local static对象会在”该函数被调用期间“”首次遇上该对象之定义式“时被初始化。
volatile
- 声明的变量可能随时变化,遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问
- 从所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。
- 应用在共享变量
template
// TODO:
virtual
- virtual析构函数用于多态基类的声明
- 被用作base class的类声明为virtual析构函数,其他类不要声明virtual析构函数(对象体积增大,不能将其传给其他语言写的函数-无移植性)
- 类的设计目的如果不是base class或具有多态性,就不该声明virtual析构函数
- 别让异常逃出析构函数
- 结束程序 try{…}catch{…;std::abort;}
- 吞掉异常 try{…}catch{…;}
- 前两者的优化:
构造/析构函数
- 构造函数调用顺序:base -> derived
- 析构函数调用顺序:derived -> base
- 在构造/析构函数中不要调用虚函数,因为这类的调用不会下降至derived class(相对的)
operator
- 令重载操作符返回一个reference to *this
- 处理自我赋值(赋值时两个为同一个对象,删除一个就都删除了)
- 证同测试,并跳过处理流程
- 用副本去操作
- copy and swap
- copy函数
- 确保复制”对象内的所有成员变量” 和 “所有base class成分”
- 不要用一个copy函数实现另一个copy函数,应该将共同机能放入第三函数,并由两个函数共同调用
指针、引用
- 指针
- 概述:指针传递参数本质上是值传递的方式,其传递一个地址值
- 流程:值传递的过程中,被调函数的形参作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为实参的一个副本
- 特点:被调函数对形参的任何操作都是作为局部参数进行,不影响主调函数是参变量的值
- 应用:
- 不改变指针指向,形参指向实参,可操作实参
- 改变指针指向,则形参作为副本,与实参无关,不可操作实参
- 引用
- 概述:引用传递本质是地址传递,传递的是实参变量的地址
- 流程:被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
- 区别
- 指针传递参数时,指针中存放的也是实参的地址,但是在被调函数内部指针存放的内容可以被改变,即可能改变指向的实参,所以并不安全
- 而引用则不同,它引用的对象的地址一旦赋予,则不能改变
双指针、指针引用
// TODO:
条件编译
- 当标识符定义过了,编译程序段1,否则编译程序段2
1
2
3
4
5
程序段1
else
程序段2
资源管理
- 以对象管理资源
- 为防止资源泄露,请使用RAII对象(tr1::shared_ptr、auto_ptr),它们在构造函数中获得资源并在析构函数中释放资源
提供对原始资源的访问
问题:如何调用一个对象
1
2
3std::tr1::shared_ptr<Investment>pInv(createInvestment());
int daysHeld(const Investment* pi); // 返回投资天数
int days = daysHeld(pInv); // 调用出错编译不通过,daysHeld需要Investment*指针,传入的却是类型为tr1::shared_ptr
的对象 解决办法:
- 智能指针
- 显示转换: trl::shared_ptr和auto_ptr都提供了一个get成员函数,它会返回只能指针内部原始资源指针(的复件)
- 隐式转换: 利用重载了的取值操作符(operator->和operator*),其允许隐式转换至内部原始指针
类
1
2
3
4
5
6
7
8
9FontHandle getFont(); //API
void releaseFont(FontHandle fh); // API
class Font {
public:
explicit Font(FontHandle fh) : f(fh){} // 获取资源,pass-by-value
~Font() {releaseFont(f);} // 释放资源
private:
FontHande f; // 原始字体资源
};font类中添加两种解决办法
显式转换
1
2
3
4
5
6
7
8
9
10
11
12class Font {
public:
...
FontHandle get() const {retun f;} //显示转换
...
};
//调用
void changeFontSize(FontHandle f, int newSize); // API
Font f(getFont());
int newFontSize;
cangeFontSize(f.get(), newFontSize); //显示将Font转换为FontHandle问题: 增加泄漏字体的可能性,而Font类的主要设计目的是为了防止资源(字体)泄露
隐式转换
1
2
3
4
5
6
7
8
9
10
11Class Font {
public:
...
operator FontHandle() const {return f;} // 隐式转换
...
};
// 调用
Font f(getFont());
int newFontSize;
changeFontSize(f, newFontSize); // 将Font隐式转换为FontHandle问题:
1
2
3Font f1(getFont());
FontHandle f2 = f1; // 原意是要拷贝一个Font对象
//却反将f1隐式转换为FontHandle对象,然后才复制它
- 智能指针