以下大部分内容均来源于《C++ Primer》
文章是以读书笔记性质来巩固所学的内容。
访问控制与继承还未补完,后续会修改
抽象基类
1、纯虚函数
- 和普通的虚函数不一样,纯虚函数无须定义。
- 通过在函数体的位置(声明语句的分号之前)书写=0 就可以将一个虚函数说明为纯虚函数。其中, =0 只能出现在类内部的虚函数声明语句处:
virtual void area() = 0;
值得注意的是,我们可以为纯虚函数提供定义,不过函数体必须定义在类的外面,换句话说,我们不能在类的内部为一个=0的函数提供函数体。
2、含有纯虚函数的类是抽象基类
含有(或者未经覆盖直接继承)纯虚函数的类是抽象基类。
抽象基类负责定义接口,而它的派生类可以覆盖该接口
注意:我们不能创建抽象基类的对象!
//其中base是抽象基类,而它的派生类为基类的纯虚函数提供定义
base b1; //错误:无法定义base的对象
derive d1;//正确,derive类没有纯虚函数。
3、派生类的构造函数只初始化它的直接基类
类C具有直接基类B和间接基类A,每个派生类只负责其直接基类的构造。
#include <iostream>
using namespace std;
class A {
int x;
public:
A(int a) {
x=a;
cout<<"Constructing A"<<endl;
}
~A(){ cout<<"Destructing A"<<endl; }
};
class B:public A {
public:
B(int x):A(x){ cout<<"Constructing B"<<endl; }
};
class C :public B{
public:
C(int y):B(y){ cout<<"Constructing C"<<endl; }
};
int main(void){
C c(1);
return 0;
}
/*
Constructing A
Constructing B
Constructing C
Destructing A
此运行结果表明,在定义C的对象时,基类A,B的构造函数都被调用了
*/
访问控制与继承
每个类分别控制自己成员的初始化过程,还分别控制其成员对于派生类来说是否可访问。
1、protected成员
protected关键字希望自身成员能被自身以及它的派生类访问,而不允许除此之外的其他函数访问。规则如下:
- 和私有成员类似,受保护的成员对于类的用户来说是不可访问的。
- 和公有成员类似,受保护的成员对于派生类的成员或者友元是可访问的。
- 派生类的成员(或友元)只能通过派生类对象来访问基类的受保护成员。派生类对于它的一个基类子对象的受保护成员没有任何访问权限。
//这个例子是为了说明友元在继承体系中是不能传递的。
class Base{
protected:
int prot_mem;
};
class Sneaky : public Base{
friend void clobber(Sneaky&);//可以访问Sneaky::prot_mem
friend void clobber(Base&);//不能访问Base::prot_mem
int j; //默认是私有成员
};
//正确:clobber能访问Sneaky对象的private和protected成员
void clobber(Sneaky &s){ s.j = s.prot_mem = 0;}
void clobber(Base &b){ b.prot_mem = 0; }
//这个例子是为了说明第三个规则。
class Base {
Base() {
a = 10;
}
protected:
int a;
};
class Derived : Base {
Base baseObject;
void foo() {
a = 11; // target object is self (kind of Derived)
baseObject.a = 12; // target object is kind of Base
}
};
/*
注意在Derived 的 foo() 的方法里,第一句是可以编译通过的,第二句不行,
会报错这正是因为,第一句a = 11操作的对象是Derived类型,因此他可以看到从Base继承下来的protected a成员,
第二句baseObject.a = 12是不行的,因为他的操作对象明确地是一个与自身self无关的另一个Base类型对象,a成员是不可见的。
*/
作者:晶龙
链接:https://www.zhihu.com/question/37051531/answer/70303204
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
即派生类的成员和友元只能访问派生类对象中基类部分的受保护成员;对于普通的基类对象中的成员不具有特殊的访问权限。
2、public、private、protected继承
某个类对其继承而来的成员的访问权限受到两个因素影响:一是在基类中该成员的访问说明符,二是在派生类的派生列表中的访问说明符。
派生访问说明符对于派生类的成员或者友元能否直接访问直接基类的成员没有影响,对基类成员的访问权限只与基类的访问说明符有关。
这段话有些晦涩,换句话说就是即使是private继承,派生类成员也可以访问基类的public和protected成员,只是这些成员在派生类中的访问权限变成了private(类成员可以访问自身类中的私有成员)。而派生类构造的对象则不能访问(类外不能访问类中的私有成员)。
则派生访问说明符的目的是控制派生类用户(对象)(包括派生类的派生类在内)对于基类成员的访问权限。
Pub_Deri d1;//继承自Base的成员是public的
Priv_Deri d2;//继承自base的成员是private的
d1.pub_mem();//正确:pub_mem在派生类中是public的
d2.pub_mem();//错误:pub_mem在派生类中是private的
派生访问说明符还可以控制继承自派生类的新类的访问权限。
3、派生类向基类转换的可访问性
假定D继承于B
1.只有当D公有继承B时,用户代码才能使用派生类向基类的转换;如果D继承于B的方式是受保护的或者私有的,则用户代码不能使用该转换。
2. 不论D用什么方式继承B,D的成员函数和友元都能使用派生类向基类的转换;派生类向其直接基类的类型转换对于派生类的成员和友元永远都是可访问的。
3. 如果D继承B的方式是公有或者受保护的,则D的派生类的成员函数和友元也可以使用D向B的类型转换,反之,如果继承方式是私有,则不能使用。
//以上例子相应的代码
class Base{
public:
void pub_mem();
protected:
int prot_mem;
private:
char priv_mem;
};
struct Pub_Derv : public Base
{
int f() { return prot_mem;}
char g() { return priv_mem;}
};
struct Priv_Derv : private Base
{
int f1() const { return prot_mem;}
};
struct Prot_Derv : protected Base
{
int f2() const { return prot_mem;}
};
struct Derived_from_Public : public Pub_Derv
{
int use_base () { return prot_mem; }
};
struct Derived_from_Private : public Priv_Derv
{
int use_base () { return prot_mem; }
};
struct Derived_from_Protected : public Prot_Derv
{
int use_base () { return prot_mem; }
};
int main(void){
Pub_Derv d1;
Priv_Derv d2;
Prot_Derv d3;
Derived_from_Public dd1;
Derived_from_Private dd2;
Derived_from_Protected dd3;
Base *p = &d1;
p = &d2;
p = &d3;
p = &dd1;
p = &dd2;
p = &dd3;
return 0;
}
4、友元与继承
基类的友元在访问派生类成员时不具有特殊性,类似的,派生类的友元也不能随意访问基类的成员。
友元关系不能继承,每个类负责控制各自成员的访问权限。
class Base{
friend class Pal;
protected:
int prot_mem;
};
class Sneaky : public Base{
friend void clobber(Sneaky&);//可以访问Sneaky::prot_mem
friend void clobber(Base&);//不能访问Base::prot_mem
int j; //默认是私有成员
};
//正确:clobber能访问Sneaky对象的private和protected成员
void clobber(Sneaky &s){ s.j = s.prot_mem = 0;}
void clobber(Base &b){ b.prot_mem = 0; }
class Pal {
public:
int f(Base b) { return b.prot_mem;}
//正确,Pal是base的友元。
int f2(Sneaky s) { return s.j; }
//错误,Sneak不是base的友元
int f3(Sneaky s) { return s.prot_mem; }
//正确,Pal是base的友元。
};
上述例子中,Pal是Base的友元,所以它能访问Base的所有成员,这种可访问性包括了Base对象内嵌在派生类对象中的情况。