目录
一、三种继承方式:
二、单继承:一个派生类只有一个直接基类的情况
三、多层继承
四、多继承:由多个基类共同派生出新的派生类
五、菱形继承:不同的类在继承关系中会产生一个环
六、赋值兼容规则
1. 派生类的对象可以赋值给基类对象
2. 派生类的对象可以初始化基类的引用
3.派生类对象的地址可以赋给指向基类的指针
4.赋值兼容的易错点:
七、成员对象和继承的区别
1.以私有继承和私有成员为例:
2. 构建顺序:先从左到右,后从上到下
3.销毁顺序:和构建顺序相反
C++ 通过类派生 的机制来支持继承。被继承的类称为基类 ,新产生的类为派生类 或子类,基类和派生类的集合 称作类继承层次结构。
由基类派生出派生类,其设计形式为:
class 派生类名 :访问限定符 基类名
{ private: 成员表1; //派生类增加或替代的私有成员
public: 成员表2; //派生类增加或替代的公有成员
protected: 成员表3; //派生类增加或替代的保护成员
};//分号不可少
在继承关系中,派生类会继承基类的全部数据成员,除了构造函数和析构函数不能继承;这是因为:我们所创建派生类的对象,虽然和基类的对象有相同之处,但是仍然是不同的对象。所以,适 用于基类的构造函数和析构函数不可能完全满足派生类对象的创建和消亡的善后工作。因此,构造函数 和析构函数不被继承。
1.公有继承 public:
基类的公有成员在派生类中仍为公有,基类的保护成员在派生类中仍为保护 ,基类的私有成员被继承后,不允许在派生类中访问。
2.保护继承 protected:
基类的公有成员在派生类中变为保护,基类的保护成员在派生类中仍为保护 ,基类的私有成员被继承后,不允许在派生类中访问。
3.私有继承 private:
基类的公有成员在派生类中变为私有,基类的保护成员在派生类中变为私有 ,基类的私有成员被继承后,不允许在派生类中访问。
不论哪种继承,基类的私有成员都会被继承,但是不允许派生类之类访问基类的私有成员。
例:
class Object
{
private: int a;
public: int c;
};
class Base : public Object //只继承一个基类
{
private: int x;
public: int z;
};
int main() {
Object obj;
Base base;
return 0;
}
派生类base 会继承基类 obj 的所有数据成员,如下图:派生类会先创建一个不具名基类对象,并用基类名Object 进行标识。
class Object
{
private: int a;
public: int c;
};
class Base : public Object
{
private: int x;
public: int z;
};
class Test : public Base
{
private:int value;
}
int main() {
Object obj;
Base base;
Test test;
return 0;
}
多层继承关系中,构建 tes t对象时,会先构建其父类,然后在构建 test;
按照图示顺序进行构建:
class Object
{
private: int a;
};
class Base :
{
public: int z;
};
class Test :public Object , public Base //由多个父类派生
{
private:int value;
}
int main() {
Test test;
return 0;
}
如图:
如图:
class Base
{
private:
int val=7;public:
string w="hello";
};
class Test1 : public Base
{
private:
int x=0;
};
class Test2 : public Base
{
private:
int y=1;
};
class Bing : public Test1,public Test2
{
private:
int z=2;
};int main()
{
Bing s1;return 0;
}
创建顺序如下图:
我们发现 s1对象中由两个 Base 类,他们的数据成员内容相同,如果想通过s1对象访问Base中的公有数据成员w,必须明确指出其类名
如图:
而,作为设计人员,我们不想让Base被s1 对象继承多次(重复的数据且内存开销大),而只被继承一次,有两种解决方法:
1.虚基类
2.避开这种菱形继承
大前提:公有继承;
例:
class Base
{
private:
int val=7;
public:
string w = "hello";
};
class Test : public Base
{
private:
int x=0;public:
int root=11;
};int main()
{
Test test;Base base;
base = test; //派生类的对象可以赋值给基类对象。
Base *p = &test; //派生类对象的地址可以赋给指向基类的指针。
Base& b = test; //派生类的对象可以初始化基类的引用。
return 0;
}
派生类的数据成员个数始终>=基类数据成员个数,在赋值时会产生截断,只将公共部分进行赋值,类似于这样
b 是派生类 test 的别名,由于 b的类型为Base基类,所以 通过 b 只能访问Base基类中的数据成员,不可以访问派生类中新增的数据成员。
如图:
p指向test的首地址,由于p的类型是基类指针,所以只能访问基类Base的数据成员。
class object
{
int value;
public:
object(int x = 0) : value(x) {
cout << "Creat object " << endl;
}
~object()
{
cout << "Destory object " << endl;
}
};
class base : public object
{
int xval;
public:
base(int x = 10) :object(x), xval(10) {
cout << "Creat base" << endl;
}
~base()
{
cout << "Destory base" << endl;
}
};
当我们用new运算符申请派生类对象并将他赋给基类指针,调用delete 回收空间时,会发现只调用了基类的析构函数,并没有调用派生类的析构函数,就会出现内存泄漏问题。
此时,需要将p强转为派生类指针,即可避免内存泄漏。
class Base{
private:
int val = 7;
protected:
int op = 2;
public:
string w = "hello";
};
class Test : private Base{
private:
int x = 0;
Base ba;
public:
int root=11;
void setval()
{
Base::w = "world";
ba.w = "newdata";
ba.op = 12; //error 不可访问保护成员
Base::op = 16; //可以访问保护成员
ba.val = 12; //error 不可访问私有数据成员
Base::val = 12; //error 不可访问私有数据成员
x = 13;
}
};int main()
{
Test test;return 0;
}
保护继承和保护成员,公有继承和公有成员类似。
即:当前类的成员对象的保护属性在当前类中不可访问