1. 构造 函数 初始化所有数据成员 (是)
a。 构造函数是用一种明确定义的状态来设置对象
b。 对象的状态由数据成员反应
2. 析构函数 (是)
a. 类 是否分配了资源,会不会自动释放?
b. 构造函数里面包含了new表达式的类,析构函数应该加上delete表达式
3. 虚析构函数 (情况)
a. 绝不会用作基类的类,不需要需析构函数
b. 任何虚函数,只有派生时才有用,虚函数是动态绑定的基础
c. 声明基类指针指向的派生类对象时,delete 此指针会掉用基类的虚构函数,不会调用派生类的析构函数
派生类的资源不会被销毁,内存泄露
d. 虚析构函数通常是空的
Base* base[2] = {
new Derived1(),
new Derived2("Bob")
};
for (int i = 0; i != 2; ++i) {
delete base[i];
}
备注:
也 就是说,在基类的析构函数为非虚析构函数的时候,并不一定会造成内存泄漏;当派生类对象的析构函数中有内存需要收回,并且在编程过程中采用了基类指针指向派生类对象,如为了实现多态,并且通过基类指针将该对象销毁,这时,就会因为基类的析构函数为非虚析构函数而不触发动态绑定,从而没有调用派生类的析构函数而导致内存泄漏。
(基类,如果虚析构函数,就会调用派生类的虚构函数)
虚析构函数,增加开销
4. 复制构造函数
a. 构造函数内分配资源,需要复制构造函数
b. 复制类的对象时,数据成员以及基类对象不能全部复制,需要复制构造函数和
c. 有非空析构函数的类,一般需要复制构造函数
d. 复制构造函数一般需要定义赋值操作符。
X(const X &);
X& operator=(const X&);
5. 赋值操作符:
a. 类的对象赋给本身,注意不要错误删除指针?
6. 删除数组 delete[]
7. 复制构造函数,赋值操作符 参数类型加const
a. 复制对象不会改变原对象
b. 绑定一个非const引用到一个临时对象是非法的
X(const X &);
X& operator=(const X&);
8. 函数的引用参数,必须是const吗?
a. 函数想改变参数,不必使用
9. 成员函数为const ?
a. 成员函数,不用修改对象,用const
int length() const
代理类 (surrogate): 一个容器中,存储不同对象
1. 容器 通常包含一种类型对象
virtual X()=0; 空的,可以不定义
2. 虚复制函数
a. 每个派生类中均添加一个新的成员函数copy,指向派生类的新建副本
Vehicle * Truck::copy() const
{
return new Truck(*this);
}
b. 基类一定要有虚析构数。
3. 代理类(复制对象时)
1. 定义行为与基类相似,并且潜在的表示了所有基类对象的东西,这种类的对象叫做代理
2. 虚函数copy完成复制工作
Class VehicelSurrogate{
public:
VehicelSurrogate();
VehicelSurrogate(const Vehicle&) //为继承Vehicle的类的对象创建代理
~VehicelSurrogate(),
VehicelSurrogate(const VehicelSurrogate&);
VehicelSurrogate & operate=(const VehicelSurrogate&);
private:
Vehicel *vp;
};
3. 空代理(类似零指针)
VehicelSurrogate::VehicelSurrogate():vp(0){}
VehicelSurrogate::VehicelSurrogate(const Vehicle& v):vp(v.copy()){}
VehicelSurrogate::~VehicelSurrogate(){delete *vp}
VehicelSurrogate::VehicelSurrogate(const VehicelSurrogate& v):
vp(v.vp?v.vp->copy():0){}
VehicelSurrogate::VehicelSurrogate& operate=(const VehicelSurrogate& v)
{
if(this!=&v)
delete vp;
vp=(v.vp?v.vp->copy():0);
}
return *this;
}
a. 对copy调用是一个虚拟调用(因为基类是虚基类,对象不存在)
b. v.vp 为1,v.vp->copy()才能被调用
c. 赋值操作符检测,确保没有把代理赋给自身
--------------------------------------------------------------
句柄 handle: 为了避免不必要的对象复制(重新读) 6,7 章
**********************************
--------------------------------------------------------------
1. 获取对象: operator->
class Handle{
public:
operate*->();
}
2.
--------------------------------------
友元:friend
1. 友元函数: 不是类的函数,却能访问类中的所有成员
class A
{
public:
friend void set_show(int x, A &a); //该函数是友元函数的声明
private:
int data;
};
void set_show(int x, A &a) //友元函数定义,为了访问类A中的成员
{
a.data = x;
cout << a.data << endl;
}
int main(void)
{
class A a;
set_show(1, a);
return 0;
}
2. 友元类
友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。
关于友元类的注意事项:
(1) 友元关系不能被继承。
(2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
(3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明。
#include <iostream>
using namespace std;
class A
{
public:
friend class C; //这是友元类的声明
private:
int data;
};
class C //友元类定义,为了访问类A中的成员
{
public:
void set_show(int x, A &a) { a.data = x; cout<<a.data<<endl;}
};
int main(void)
{
class A a;
class C c;
c.set_show(1, a);
return 0;
}
3. 友元成员函数
使类B中的成员函数成为类A的友元函数,这样类B的该成员函数就可以访问类A的所有成员了。
#include <iostream>
using namespace std;
class A; //当用到友元成员函数时,需注意友元声明与友元定义之间的互相依赖。这是类A的声明
class B
{
public:
void set_show(int x, A &a); //该函数是类A的友元函数
};
class A
{
public:
friend void B::set_show(int x, A &a); //该函数是友元成员函数的声明
private:
int data;
void show() { cout << data << endl; }
};
void B::set_show(int x, A &a)
//只有在定义类A后才能定义该函数,毕竟,它被设为友元是为了访问类A的成员
{
a.data = x;
cout << a.data << endl;
}
int main(void)
{
class A a;
class B b;
b.set_show(1, a);
return 0;
}