派生:公有派生
1 派生类的对象具有基类的数据成员
2 派生类的对象可以使用基类的方法
3 派生类需要有自己的构造函数
4 派生类可添加额外的数据成员与成员函数
====================================================================
关于派生类的构造函数:
1 基类对象首先被创建
2 通过“成员初始化列表”,派生类构造函数将基类对象初始化信息传递给基类构造函数
3 派生类构造函数应初始化派生类新增的数据成员
构造函数+成员初始化列表,语法:
derived :: derived ( type1 x, type2 y) : base (x , y)
{
...
}
====================================================================
关于派生类的析构函数:
首先执行派生类的析构函数,再自动执行基类的析构函数
派生类与基类之间的特殊关系
1 派生类可以使用基类的非私有方法
2 基类指针 可以在不进行显式类型转换的情况下 指向 派生类对象
3 基类引用 可以在不进行显式类型转换的情况下 引用 派生类对象
4 基类的指针、引用只能用于调用基类方法,不能调用派生类的方法
5 派生类类型 的指针和引用不可接受 基类对象 的地址和引用
====================================================================
继承概念的概括:is a kind of(is a)模型
====================================================================
多态公有继承:两种机制
1 在派生类中可以重新定义基类的方法
2 虚方法:virtual
如果派生类中有方法是对基类方法的重写,并且方法是通过引用或者指针,而不是对象来调用的:
1 如果没有virtual,根据引用或者指针声明时候的类型来调用派生类版本的方法、基类版本的方法
2 如果使用virtual,根据引用或者指针指向的对象的类型来选择方法
====================================================================
在派生类方法中调用基类方法:
使用作用域操作符
====================================================================
虚析构函数在继承中的必要性:
确保派生类对象的析构函数也能被正确的被调用,当使用基类指针或者引用来表示派生类对象时。
====================================================================
静态联编与动态联编
向上强制转换:派生类的引用或者指针可以被转换为基类的引用或者指针
向下强制转换:基类的引用或者只恨可以被转换派生类的引用或者指针(不允许)
使用虚方法让编译器使用动态联编
静态联编与动态联编的比较
静态联编(非虚函数):效率高,
动态联编(虚函数):可以重写基类方法,但消耗更多时间,程序占用更多空间
类方法是否有关键字virtual 指出了它的派生类是否会重写该方法,因此virtual应慎用。
====================================================================
关于虚函数的注意事项:
1 在基类方法声明中使用 virtual 可以使该方法在基类以及所有派生类中是虚拟的
2 使用指向对象的引用或者指针来用虚方法,程序将根据对象类型来调用对应的方法,而不根据引用或指针类型来调用对应的方法(动态联编)。
3 如果在定义基类,应将需要在其派生类中重写的方法声明为虚拟的。
4 构造函数不能是虚函数
5 析构函数应当是虚函数
6 友元不能是虚函数,可以通过让友元函数使用虚拟成员函数来解决一些具体问题
7 在派生类中,用同名不同参数的方式重写(重新)基类函数,将使得基类的同名但不同参函数的方法被覆盖(无法被调用):
如果在派生类中重写基类方法,应确保与基类的原型完全相同,但如果返回类型是基类引用或者指针,则返回类型可以被修改为派生的引用或者指针(返回类型协变)。
如果基类的函数具有多个重载形式,则应在派生类中逐一重写,否则该基类函数的部分重载函数将被覆盖。
====================================================================
Protected关键字:
通过保护继承的派生类可以直接访问基类的保护成员,但不能直接访问基类的私有成员。
====================================================================
设计模式:单元素模式(Singleton pattern)
用于全程序只需要某个类的一个全局静态实例时
class TheOnlyInstance
{
public:
static TheOnlyInstance* GetTheOnlyInstance();
...
protected:
TheOnlyInstance() { }
private:
...
}
1 将类的构造函数声明为protected,并且省略公有构造函数,可以防止局部实例被创建
2 只能通过公有静态方法来创建类的一个全局静态实例。
TheOnlyInstance* TheOnlyInstance::GetTheOnlyInstance()
{
static TheOnlyInstance obj;
return &obj;
}
3 调用类的公有静态方法来得到单对象的地址:
TheOnlyInstance* pobj = * TheOnlyInstance::GetTheOnlyInstance();
====================================================================
关于抽象基类(ABC):
1 纯虚函数语法:类中函数声明结尾处 = 0 ,类中可以不定义该函数。
2 类声明中包含纯虚函数,则不能创建该类的对象,该类也就成为了抽象基类
====================================================================
继承下的动态内存分配:
1 基類使用動態內存分配,但是派生類不使用new:
派生類不需要定义显式析构函数,复制构造函数,和赋值操作符。
2 基类使用动态内存分配,派生类使用new:
派生类必须显式定义析构函数,复制构造函数,赋值操作符。
2.1 派生类的析构函数:处理派生类中new的数据
2.2 派生类的复制构造函数: 调用基类的复制构造函数来处理基类的动态内存分配问题
语法:
B :: B (const B & b): A ( b )
{
new(...);
strcpy(...);
}
2.3 派生类的显式复制操作符:通过显式调用基类赋值操作符来处理基类部分的动态内存分配
语法:
B & B::operator = (const B & b)
{
if (this == &b)
{
return *this;
}
A :: operator = (b);
new(...);
strcpy(...);
return *this;
}
====================================================================
Review:
编译器会自动为类生成的公有成员函数:
1 默认构造函数
2 复制构造函数
被调用情景:
2.1 将新的对象初始化为一个同类对象
2.2 按值将对象传递给函数
2.3 函数按值返回对象
2.4 编译器生成临时对象
3 复制操作符
几个重要的类方法的一些特性:
1 构造函数:不被继承
2 析构函数:应该用virtual 修饰来方便以后继承
3 转换:使用一个参数就可以调用的构造函数定义了从参数类型到类类型的转换
4 按值传递对象与传递引用:将对象作为函数参数是,使用传递引用效率更高
5 返回对象与返回引用:当返回的是函数中的局部对象时,应该返回对象,如果不是返回函数中的局部对象,则返回引用效率更高。
6 使用const:
用法:
6.1 可以用来确保方法不修改参数:
A :: A (const char *s) {...}
6.2 可以用来确保方法不修改调用它的对象:
void A :: show() const {...}
6.3 可以用来确保返回的引用或者返回的指针指向的内容不能被修改
const A & :: A abc(const A &a) const (...)
====================================================================
公有继承的考虑因素:
1 类的继承要遵循is a kind of 关系
2 构造函数,析构函数,赋值操作符不能被继承
3 虚函数:如果希望派生能够重新定义方法,则应该在基类中将方法定义为虚函数,以启动动态联编
如果不希望派生类重新定义方法,则不应该在基类中将方法定义为虚函数,提高可读性
4 析构函数:基类的析构函数应该是虚拟的,确保派生类对象可以正确的调用派生类对象自己的析构函数
5 友元函数:友元函数不能继承,可以通过强制类型转换将派生类引用或者指针转换基类引用或者指针,然后使用转换后的指针或引用来调用基类的友元函数
或者使用dynamic_cast<>
6 如何使用基类方法:
6.1 如果派生类没有重新定义该方法,则派生类对象可以直接使用继承而来的基类方法
6.2 派生类的构造函数自动调用基类的构造函数
6.3 派生类的构造函数自动调用基类的默认构造函数,如果没有在成员初始化列表中指定其他的构造函数
6.4 派生类构造函数显式的调用成员初始化列表中指定的基类构造函数
6.5 派生类方法可以使用作用域解析操作符来调用公有的和受保护的基类方法
6.6 派生类的又换函数可以通过强制类型转换,将派生类引用或者指针转换为基类引用或者指针,然后使用该引用或指针来调用基类的友元函数
====================================================================