继承
在C++中,可以利用【继承】来利用已有的一个数据类型来定义一个新的数据类型
继承的格式
class [子类类名]: [权限] [父类]
继承的分类
继承分类三类:公有继承,私有继承和保护继承
(1)公有继承
公有继承中,【基类】中的【公有成员】和【保护成员】在【派生类】中的访问权限不变,【基类】中的【私有成员】也不能被【派生类】所访问
class Base//基类
{
public:
int _pub;//公有成员 _pub
protected:
int _pro;//保护成员 _pro
private:
int _pri;//私有成员 _pri
};
class P: public Base //p类为派生类,继承于 基类 Base
{
public:
void FunTest()
{
cout << _pub << _pro << _pri;//试着访问以下三个不同权限的成员
}
};
报错:
结论:
公有继承中,【派生类】可以访问【基类】的【公有成员】和【保护成员】,不能访问【私有成员】
在具体调用时(类外),【派生类】只可以访问【基类成员】的【公有成员】
(2)私有继承
私有继承,会将【基类】中的【公有成员】变成【私有】的
class Base//基类
{
public:
int _pub;//公有成员 _pub
protected:
int _pro;//保护成员 _pro
private:
int _pri;//私有成员 _pri
};
class P: private Base //p类为派生类,继承于 基类 Base
{
public:
void FunTest()
{
cout << _pub << _pro ;//试着访问公有成员和保护成员、、发现私有继承后的公有成员在类里还可以使用
}
};
int main()
{
P p;//定义一个派生类对象 p
p._pub = 1;//访问以下p由Base类私有继承的 _pub成员
system("pause");
return 0;
}
报错:
结论:
私有继承中,【派生类】可以访问【基类】的【公有成员】和【保护成员】,不能访问【私有成员】
在具体调用时(类外),【派生类】不可以访问【基类成员】的【公有成员】,因为已被设置为【私有】(3)保护继承
保护继承中,【派生类】的【公有成员】会被设置为【保护】的
class Base//基类
{
public:
int _pub;//公有成员 _pub
protected:
int _pro;//保护成员 _pro
private:
int _pri;//私有成员 _pri
};
class P: protected Base //p类为派生类,继承于 基类 Base
{
public:
void FunTest()
{
cout << _pub << _pro ;//试着访问公有成员和保护成员、、发现私有继承后的公有成员在类里还可以使用
}
};
int main()
{
P p;//定义一个派生类对象 p
p._pub = 1;//访问一下p由Base类公有继承的 _pub成员
p._pro = 2;//访问一下p由Base类保护继承的 _pro成员
p._pri = 3;//访问一下p由Base类私有继承的 _pri成员
system("pause");
return 0;
}
报错:
继承中,构造函数和析构函数的调用顺序
用事实说话,那就直接进行测试吧
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
~Base()
{
cout << "~Base()" << endl;
}
};
class P : public Base
{
public:
P()
{
cout << "p()" << endl;
}
~P()
{
cout << "~P()" << endl;
}
};
int main()
{
P p;
return 0;
}
结果:
这里我的理解为,要生成【派生类】的对象p,那么先调用【派生类】的构造函数
又由于继承于【基类】(某些成员来自于基类),又得调用【基类】的构造函数。在【基类】生成后,才可以真正生成【派生类】的对象。
这也就是先输出Base()再输出p()的原因
析构函数也是如此
合成默认构造函数
语法规定,c++中,在没有定义【构造函数】时,编译器会自动合成【默认的构造函数】
那么,【编译器】真的遵循了吗?
在VS的几个版本中,答案是【否定】的
但也会在特定的情况下合成【默认构造函数】
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
};
class P : public Base
{
public:
};
int main()
{
P p;
system("pause");
return 0;
}
在这种情况下,编译器会合成P的默认构造函数
进入反汇编查看:
此时,我们发现了调用了P的构造函数,但是我们没有定义,所以这便是默认的
其他情况下,不会合成默认构造函数
注意:和子对象类似,在【基类】中如果没有缺省的构造函数,【派生类】中需要自行给出构造函数,并在命令行参数中调用【基类】构造函数
class Base
{
public:
Base(int i)
{
cout << "Base()" << endl;
}
};
class P : public Base
{
public:
};
int main()
{
P p;
system("pause");
return 0;
}
报错:
无法合成可用的构造函数
同名隐藏
当【派生类】的成员或变量名和【基类】冲突时,必须加限定符,否则无论如何也找不到【基类】的成员
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
~Base()
{
cout << "~Base()" << endl;
}
int _a;
};
class P: public Base
{
public :
P()
{
cout << "p()" << endl;
}
~P()
{
cout << "~P()" << endl;
}
int _a;
};
void FunTest()
{
P p;
p._a = 1;//调用了 派生类 的_a
p.Base::_a = 2;//必须加作用域限定符才能找到基类中的 _a
}
赋值兼容规则
(1)派生类对象可以直接给基类赋值
(2)基类对象可以直接引用派生类对象
多继承和菱形继承
多继承,故名思议,就是一个【基类】有多个【派生类】
然而,可能出现【菱形继承】
class A
{
public:
int _a;
};
class B1:public A
{
public:
int _b1;
};
class B2 :public A
{
public:
int _b2;
};
class C : public B1, public B2
{
public:
int _c;
};
int main()
{
/*测试这几个类的大小*/
cout << sizeof(A) << endl;//4
cout << sizeof(B1) << endl;//8
cout << sizeof(B2) << endl;//8
cout << sizeof(C) << endl;//20
C c;
c.B1::_a = 1;
c._b1 = 2;
c.B2::_a = 3;
c._b2 = 4;
c._c = 5;
return 0;
}
如果我们没有做任何处理,那么,访问A的成员_a必须加限定符
以便确定是B1类中的,还是B2类中的(这就产生了二义性)
经过处理后:
class A
{
public:
int _a;
};
class B1 :virtual public A
{
public:
int _b1;
};
class B2 :virtual public A
{
public:
int _b2;
};
class C : public B1, public B2
{
public:
int _c;
};
int main()
{
cout << sizeof(A) << endl;//4
cout << sizeof(B1) << endl;//12
cout << sizeof(B2) << endl;//12
cout << sizeof(C) << endl;//3*8 = 24
//加了virtual后,为B1、B2增加了默认的构造函数
//变化如下
//之前的构造函数,会传入地址.而虚拟继承的多传了一个1
//B1对象会存一个虚拟指针,指向一块空间,空间存的是偏移的地址(自己和基类)
//自己元素的位置和基类互换了
C c;
c._a = 1;
c._b1 = 2;
c._b2 = 3;
c._c = 4;
return 0;
}
图示如下:
总结:
1.对于公有继承方式:
·【基类】成员对其【对象】的可见性与一般类及其【对象】的可见性相同,【公有成员】可见,其他成员不可见。这里【保护成员】与【私有成员】相同。
·【基类】成员对【派生类】的可见性对【派生类】来说,【基类】的【公有成员】和【保护成员】可见:
【基类】的【公有成员】和【保护成员】作为【派生类】的成员时,它们都保持原有的状态;【基类】的【私有成员】不可见:
【基类】的【私有成员】仍然是私有的,【派生类】不可访问【基类】中的【私有成员】。
·【基类】成员对【派生类】【对象】的可见性对【派生类】【对象】来说,【基类】的【公有成员】是可见的,其他成员是不可见。
所以,在公有继承时,【派生类】的【对象】可以访问【基类】中的【公有成员】;【派生类】的成员函数可以访问【基类】中的【公有成员】和【保护成员】。
2.对于私有继承方式:
·【基类】成员对其【对象】的可见性与一般类及其【对象】的可见性相同,【公有成员】可见,其他成员不可见。
·【基类】成员对【派生类】的可见性对【派生类】来说,【基类】的【公有成员】和【保护成员】是可见的:【基类】的【公有成员】和【保护成员】都作为【派生类】的【私有成员】,并且不能被这个【派生类】的子类所访问;【基类】的【私有成员】是不可见的:【派生类】不可访问【基类】中的【私有成员】。
·【基类】成员对【派生类】【对象】的可见性对【派生类】【对象】来说,【基类】的所有成员都是不可见的。
所以,在私有继承时,【基类】的成员只能由直接【派生类】访问,而无法再往下继承。
3.对于保护继承方式:
这种继承方式与私有继承方式的情况相同。两者的区别仅在于对【派生类】的成员而言,
·【基类】成员对其【对象】的可见性与一般类及其【对象】的可见性相同,【公有成员】可见,其他成员不可见。
·【基类】成员对【派生类】的可见性对【派生类】来说,【基类】的【公有成员】和【保护成员】是可见的:【基类】的【公有成员】和【保护成员】都作为【派生类】的【保护成员】,并且不能被这个【派生类】的子类所访问;【基类】的【私有成员】是不可见的:【派生类】不可访问【基类】中的【私有成员】。
·【基类】成员对【派生类】【对象】的可见性对【派生类】【对象】来说,【基类】的所有成员都是不可见的。
所以,在保护继承时,【基类】的成员也只能由直接【派生类】访问,而无法再往下继承。