1. 拷贝构造函数
1.1. 拷贝构造函数的作用是通过一个对象生成另外一个对象,或者是对象作为函数参数或者函数返回值进行值传递时。
1.2. 拷贝分为深拷贝(值拷贝)和浅拷贝(位拷贝):
当类成员含有资源(指针)时,其中深拷贝(值拷贝)是将类成员资源复制一份,因此指针指向的是不同的地址,而浅拷贝(位拷贝)只是指针地址的拷贝,因此指针指向相同的地址。
1.3. 浅拷贝与深拷贝的经典写法(参考string类源码):
假设定义一个class MyString,类定义如下:
#ifndef _MY_STRING
#define _MY_STRING
#include <iostream>
using namespace std;
class MyString
{
public:
MyString() : m_head(NULL), m_len(0) {}
MyString(char* s);
const MyString& operator= (const MyString& s);
public:
char* m_head;
size_t m_len;
};
#endif // __MY_STRING
深拷贝函数的写法如下:
const MyString& MyString::operator= (const MyString& s)
{
if(this != &s)
{
m_len = s.m_len;
m_head = new char[strlen(s.m_head)+1];
strcpy(m_head, s.m_head);
}
return *this;
}
浅拷贝函数的写法如下:
const MyString& MyString::operator= (const MyString& s)
{
if(this != &s)
m_head = s.m_head;
return *this;
}
1.4. 如果没有自己定义拷贝构造函数,C++编译器自动分配的是浅拷贝(位拷贝)。
当类成员含有指针变量时,需要记得自己定义拷贝构造函数,因为如果使用默认的浅拷贝,会造成问题如下:
(1)当销毁两个对象时,指针成员变量指向的同一段内存被释放两次;
(2)当通过其中的一个对象的指针改变其所指向对象的内容时,另外一个对象的指针成员所指向的内容也跟着发生了变化。
为了避免上述问题,除了自己定义深拷贝的拷贝构造函数外,还可以用写时拷贝(copy on write)来解决浅拷贝带来的问题。
2. const的作用:
2.1. const与变量:
(1)const修饰变量,则该变量是常量。
(2)const修饰指针,如果const在*左边,表示指针所指向的对象是一个常量,不可修改,例如const int* a或者是int const *a;如果const在*右边,表示 指针是一个常量指针,所指向的地址不可修改,例如int* const a。
2.2. const与函数:
2.2.1. 修饰函数输入参数时,保证函数内部无法修改该参数。
2.2.2. 修饰函数返回值时:
(1)如果返回值是对象或值,没有任何意义。
(2)如果返回值是指针,则返回值指针指向的内容是常量,不可被修改。
(3)返回值是引用,用const修饰引用并返回可以比返回对象提高效率,因为无需进行对象传递,对象传递过程中涉及到对象的拷贝和销毁,但是需要注意的是如果返回的是对象的引用,一定要加const修饰,表示只读,来避免修改掉所读变量的危险,引起数据错误。
2.3. const与类:
2.3.1. const修饰成员函数放在成员函数最后,表示该函数不能修改类的属性。
3. C++重载,重写和隐藏的概念:
3.1. 需要特别注意的点:
由于C++具有隐藏的特点,所以某个类定义了带参数的构造函数,而没有定义无参数的构造函数,则初始化一个对象时,无法调用其无参数的构造函数,因为带参数的构造函数已经将基类(可能不是用户定义的而是系统定义的)的不带参数的构造函数隐藏了。
4. i++和++i的区别:
i++先把i值拿来执行所在的语句,执行完再+1;++i先将i+1,然后再拿来执行所在的语句。
5. C++用throw抛出异常。
6. 宏:
6.1. 宏的作用:
(1)批量替换程序中的常量;
(2)减少函数调用的开销。
6,.2. 宏定义需要注意的地方:
(1)宏定义中的空格不容忽视;
(2)把宏定义中每个变量和表达式都用括号括起来;
(3)类型定义最好不用宏而应该用typedef,因为涉及到指针类型的时候有可能会出现问题,例如:
#define int* INTPTR;
INTPTR ptr1, ptr2 ==> int* ptr1 && int ptr2
7. 类析构函数的调用:
如果是new生成的对象,则在delete指向该对象的指针时调用析构函数;
如果是静态声明一个对象,例如class Test声明一个对象Test test,则在此对象出作用域的时候自动调用类的析构函数。
8. raii用法:
8.1. 字面解释:
资源获取即初始化
8.2. 作用:
避免当因为某些异常情况退出时,没有及时释放程序中的资源。
8.3. 实现方法:
将资源获取,使用和释放封装成类,当由于异常情况退出时,由于静态声明的对象在出作用域时会自动调用类的析构函数,所以可以在析构函数中保证资源的释放。
8.4. 注意事项:
这种类不允许在堆上创建对象,也不允许复制和赋值操作,所以一般定义一个基类,使派生类私有继承基类来保证这些注意事项,然后在派生类中覆盖类的构造函数,析构函数,实现操作资源的函数。
9. 应避免垂悬指针和野指针:
9.1. 垂悬指针指的是指向已经被释放的资源,应当重新将其指向NULL,尤其要注意函数中的局部变量存在于堆栈中,函数调用结束时这些变量会销毁,指向这些变量的指针如果从返回值中返回就变成了垂悬指针。
9.2. 野指针指的是未进行初始化的指针。
9.3. static指针未进行显示初始化的时候会被初始化为0。