一、如果类里有引用成员或是const成员,则要自定义一个copy赋值 运算符;
因为如果不自定义,则将一个对象赋给另一个对象时,可能会改变引用对象所指向的对象,这是不可能的!
二、不使用编译器自动生成的函数:如构造、赋值运算符
法1:将这些函数显示设为private并且不实现
但不安全,因为成员函数或是friend函数可以调用;
法2:用一个基类,在基类中将这些函数设为private,则子类中不会自动生成这些函数,且子类成员函数或是friend函数也无法调用这些函数;
三、将基类析构函数设为virtual ~, 防止局部析构
非virtual析构函数下:经常会有基类指针指向子类对象,这里如果删除指针,则只会调用基类的析构函数,而子类的部分不被删除,出现局部删除现象,而造成内存泄露;
解决这个现象的办法是将基类的析构函数设为virtual
多态:由基类接口处理子类对象;如果基类没有任何多态的虚函数,可以定义一个虚析构函数;
四、创建一个类来管理对象
在该类在块区结束点时调用析构函数自动处理要完成的功能如释放内存等;
vector<A> v[10]; 如果在析构V的内容时,第一个在析构时抛出异常,则可能会直接退出,后面的V没有释放掉,出现内存泄露;
1、 如果析构函数可能抛出异常,则析构函数应该捕捉任何异常,然后不传播或是结束程序;
2、如果要对某个操作函数运行期间的异常做出反应,则类应该提供一个普通函数(而非析构函数中)执行该操作;
9、确定构造与析构函数里没有虚函数
构造:
基类构造函数内如果有虚函数,则在子类构造时,调用的虚函数仅仅是基的的函数而不是子类的函数,因为基类是先于子类构造的;即便这个虚函数是由成员函数调用的也不行;
在子类构造时让基类的构造函数调用子类的函数:
因为构造函数里的虚函数不会调用到子类,因此只能通过在子类构造函数中调用基类的构造函数,同时把相应的参数传递进去,让基类的函数完成相应子类函数的功能;
10、自我赋值
类中有指针,=运算符要分配内存时;
当函数参数里有多个对象,确定在自我赋值的情况下也可以正确执行;
最基本的是操作是:
<span style="font-size:14px;">A& A::operator=(A& a)
{
if(&a == this) return *this;
delete pm;
pm = new int(*a.pm);
return *this;
}</span>
上面的代码存在一个问题:如果new失败,则pm指向一个删除了的内存;因此可以改成下面的:
<span style="font-size:14px;">A& A::operator=(A& a)
{
int *po = pm; //保存一个副本
pm = new int(*a.pm); //失败也可以正确
delete po;
return *this;
}</span>
11、子类要构造基类中的成员
在子类的构造函数初始化,同时要初始化基类的对象;当基类成员多时,可以直接调用基类的构造函数;
不然子类对象只初始化了子类的成员而基类成员将调用基类的default Constr;
对于copying构造(cpoy 构造、复值运算符一样);
class A
{
public:
A& operator=(A& a);
int a;
A(){};
int* pm;
};
class B : public A
{
public:
B():A() // 对基类成份进行构造
{};
B& operator=(B& b)
{
A::operator=(b); // 对基类成分进行复值
//再复值子类
}
};