类的继承,是新类从已有类那里得到已有的特性,或从已有类产生新类的过程。原有类称为基类或父类,新类称为派生类或子类。
子类通过类派生列表确定子类继承了哪些类。类派生列表的形式如下
class 子类名:访问说明符 基类1名, 访问说明符 基类2名, 访问说明符, 基类3名 {
//...
}
示例
class base1
{
public:
base1(){}
~base1(){}
};
class base2
{
public:
base2(){}
~base2(){}
};
class base3
{
public:
base3(){}
~base3(){}
};
class derive:public base2
{
public:
derive(){}
~derive(){}
};
class derive1:public base1, protected base2, private base3
{
public:
derive1(){}
~derive1(){}
};
上述代码表明:C++既有单一继承,也有多重继承,而且继承也可以分为公有继承,保护继承和私有继承
一、子类会继承父类的哪些成员
当一个子类继承父类时,父类中的所有非static成员函数和成员变量会被子类全部继承,但是子类不一定能够访问到全部的非static成员
示例
class base2
{
public:
base2(){cout<<__func__<<endl;}
~base2(){cout<<__func__<<endl;}
void func2() {cout<<__func__<<endl;
func2pri();
}
int a;
protected:
void func2pro() {cout<<__func__<<endl;}
int b;
private:
void func2pri() {cout<<__func__<<endl;}
int c;
};
class derive:public base2
{
public:
derive(){cout<<__func__<<endl;func2pro();/*func2pri();*/}
~derive(){cout<<__func__<<endl;}
//void func2() {cout<<"func2 in base3"<<endl;}
};
int main(int argc, char const *argv[])
{
derive t;
cout<<sizeof(t)<<endl;
t.func2();
cout<<t.a<<endl;
//t.func2pro();
return 0;
}
输出结果表明:
1、当一个子类被创建时,先分配存储空间(即从基类继承下来的数据大小加上子类自身数据成员的大小)。然后调用基类的构造函数来初始化从基类继承下来的数据。,接着调用子类的构造函数来初始化子类中的数据成员。此时,子类对象创建完成。子类的sizeof是12,说明父类中的数据都被子类继承了
2、protected成员可以在子类中被访问,但是不能在其他类中或者其他作用域中被访问,如果将主函数中的t.func2pro();的注释解除,会出现如下错误
提示父类中的func2pro是protected。不能在main函数中被访问
3、即使子类继承了父类的private成员,但是依旧不能访问父类的private成员,如果将第31行的func2pri();注释解除,则会出现以下错误
提示父类中的func2pri是private,只能在类内被访问
4、如果在子类中实现一个和父类的同名函数,那么会优先调用子类的同名函数,不会调用父类的同名函数,如果将第33行的void func2() {cout<<"func2 in base3"<<endl;}注释解除,则会出现以下输出结果
5.当一个子类被销毁时,会先调用子类的构造函数,将子类对象中子类的成员销毁,然后再调用父类的构造函数,将子类对象中父类对应的成员销毁,此时,子类对象被完全销毁
所以,子类对象创建时,会从对应的基类部分到子类部分一步一步创建,销毁时,也会从对应的子类部分到基类部分一步一步销毁;子类即使继承了父类的所有非static成员,但是子类并不能访问所有的非static成员。子类调用一个成员时,会现在子类对应的部分查找是否有对应的成员,如果有,直接调用,如果没有,会继续到父类中继续查找,如果有,调用父类对应的成员,否则报错
二、protected关键字
该关键字就是为继承而生的。子类能够访问父类的公有成员,不能访问父类的私有成员,但是C++还希望父类的有些成员只有子类能访问,其他类或者作用域不能访问,所以protected产生了
三、类的继承与static成员
如果基类中定义了static成员,那么,无论基类有多少个子类,每个static成员都只有一个实例
示例
class base1
{
public:
base1(){}
~base1(){}
static void sfunc() {cout<<__func__<<endl;}
static int st;
};
int base1::st=10;
class derive2:public base1
{
public:
derive2(){cout<<__func__<<endl;}
~derive2(){cout<<__func__<<endl;}
};
int main(int argc, char const *argv[])
{
derive2 t;
t.sfunc();
cout<<t.st<<endl;
derive2::sfunc();
base1::sfunc();
cout<<&derive2::st<<endl;
cout<<&base1::st<<endl;
return 0;
}
static成员既可以通过基类来访问,也可以通过子类来访问,而且打印出的地址都是相同的,这都是因为static成员在继承体系中只有一个实例,无论有多少个子类,static成员都是共享的
四、什么样的类可以被当做基类
如果一个类可以被作为基类,那么该类必须已经定义,而不是仅仅声明;一个仅仅被声明而没有被定义的类不能做父类
示例
class base4;
class derive3:public base4
{
public:
derive3();
~derive3();
};
因为子类要从基类中获得成员,而一个类仅仅声明,没有实现,那么子类就无法知道基类中到底有啥,所以提示基类不完整。因为类在定义的过程中也是不完整的,所以一个类不能继承它本身
五、如何防止继承
如果不想让一个类当父类,可以在类名后加个关键字final
class base3 final
{
public:
base3(){}
~base3(){}
};
class derive3:public base3
{
public:
derive3();
~derive3();
};
参考
《C++ Primer》
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出