1、C++书籍
语法书籍:
- 《C++Primer》
- 《learning Program》
提升书籍
- 《effective C++》
标准库
- 《The C++ standard Libaray》
2、头文件的规范
#ifndef __CLASS__//防御式申明
#define __CLASS__
class(){}
#endif
3、初始化列表
利用构造函数的初始化列表来初始化,可以提高效率,而且如果类内含有没有默认构造函数的对象,必须用这种方式来初始化数据对象。应该是在进入构造函数体之前,类内所有数据对象都已经经历了初始化阶段。
class(int a,classB b):A(a),B(b){};
4、不能重载有默认值的构造函数
违背了唯一最佳函数匹配原则
class(int r = 0):R(r){};
class(){};
//以上两个函数不能同时出现
5、函数后面加const
函数后面加const表明在这个函数里面不能改变对象的data,通常与const对象配合使用
const class a;
void read() const{return a.data;}
6、参数传递传值还是引用?
传引用比传值效率高,因为传值需要在栈内建立一个大小相同的内存,但是传引用只是一个地址,一般对于字符串,自定义的类对象传引用更加合适,同时为了防止传引用被改变原地址内容的值,可以用const修饰。
classA(const classB &b):B(b){};
7、不能返回在函数里临时创建对象的引用
不能返回在函数里临时创建对象的引用,因为临时对象函数结束就会被销毁,其地址没有意义。其他情况尽量多返回引用。
8、相同的class各个对象之间互为友元
class A {
private:
int i;
public:
int f(A &a){return a.i}
}
9、代码规范总结
- 数据private
- 初始化列表
- 传参多引用
- const修饰不能少
10、返回指针对象的引用
传递者不需要知道接收者按照什么方式接受
inline complex &
complex::operator += (const complex &c)
{return *this}
返回引用,引用返回的应该是一个左值对象,而value是一个右值对象。
函数返回值是引用(引用当左值),
当一个对象被用作右值的时候,用的是对象的值(内容),
当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。
11、操作符重载的成员函数方法和友元函数方法以及普通方法
成员函数方法多一个this指针,代表左操作数,友元函数和普通函数都没有这个this指针,而且普通函数不能访问私有变量。
C++的IO库操作符不能使用成员函数的方法重载,只能用友元函数重载(如果想访问私有变量的话)
12、设计一个函数需要思考的问题
- 函数后面要不要加const
- 函数参数要不要加const
- 函数返回:要不要,能不能返回引用
13、若类内有指针,则必须自己写拷贝构造函数和拷贝赋值函数
因为默认拷贝构造函数会直接赋值指针内的地址,这样两个对象内的指针实际上指向同一块内存,非常不安全。
14、深拷贝和浅拷贝
深拷贝创建一块新内存存储内容。浅拷贝,拷贝指针地址,指向同一块内存。
15、赋值重载操作符
- 释放之前的内存空间
- 创建新的大小合适的内存空间
- 将右边对象的内容拷贝到上面创建的空间中
但是如果是自我赋值,需要用一下检测避免
if(this == &args)
return *this;
16、堆和栈
一般栈是某一个作用域的内存空间,保存参数,返回地址,以及局部变量,一个函数可以形成一个栈空间。函数结束则栈空间销毁。
堆是由系统内存分配的全局空间,可动态分配,其中的内存不会因为某个局部作用域结束而受到影响。
{
class a;
class *p = new class();
}
//a 和 p 都是存在于局部作用域中,也就是栈中,但是 new后产生的对象存在于堆中,所以程序结束之后 指针P被销毁,指针P中的内容是堆中的一个地址,这时候这个堆的这块地址就无法被销毁,造成了内存泄漏
17、new和delete函数干了什么事情
- new函数
分配内存,转型,调用构造函数。
- delete函数
调用析构函数(析构函数可能会释放对象内的动态分配的内存)
释放这个对象的动态内存
18、delete[] 与delete
当创建动态对象数组时,如果使用delete 和delete[]都能删除new分配的动态内存,但是使用delete时,只会调用一次对象的析构函数,这个时候如果对象内有指针指向其他动态内存则会发生内存泄漏,但是使用delete[],则会产生了几个对象就调用几次析构函数。
19 、类内静态函数中有静态变量
class A{
static A& getAa();
}
A& A::getAa(){
static A a;
return a;
}
20、复合类关系下的构造函数和析构函数
构造由内而外,及先调用复合类(组成类)的默认构造函数,再调用类本身的构造函数,析构由外而内。
21、类委托关系(复合引用关系)
22、继承中基类的析构函数必须是virtual函数
23、继承中基类的函数类型
- 非虚函数:不希望子类重写
- 虚函数:有默认定义,希望子类重写
- 纯虚函数:没有默认定义,子类必须重写
虚函数: virtual fun(){};
纯虚函数: virtual fun() = 0;
非虚函数: fun(){};
24、转换函数
将一个类转换为另一个类的实例,如下图黄色表示:
25、explicit
明确函数的使用,编译器不能进行转换用作其他用途。如下使用explicit是为了避免二义性
26、函数模板 类模板 成员模板
//函数模板
template <class T>
const T& min(const T& a;const T& b)
{ return b < a?b:a}
min(r1,r2)//编译器可以进行实参推导
//
以上程序调用首先是创建两个子类的pair,然后用子类的pair去构造一个父类pair,由于子类型可以转化为父类型,所以构造函数正常工作。
27、模板特化,偏特化
增加T的一些特例
28、reference
//同时出现下面两个函数会引起二义性
void fun(int &a);
void fun(int a);
29、继承类 复合类的构造函数 析构函数执行次序
构造函数: 父类 复合类 自己
析构函数: 自己 复合类 父类
30、虚函数
由于子类型可以转化为父类型,所以构造函数正常工作。
27、模板特化,偏特化
增加T的一些特例
28、reference
//同时出现下面两个函数会引起二义性
void fun(int &a);
void fun(int a);
29、继承类 复合类的构造函数 析构函数执行次序
构造函数: 父类 复合类 自己
析构函数: 自己 复合类 父类