一、继承
概念:继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现由简单到复杂的认知过程。
继承定义格式如下图:
1.继承关系和访问限定符如下图:
<1>、先举一继承的个例子(单继承)如下:
class B
{
public:
void FunTest1()//函数
{
cout<<"FunTest"<<endl;
}
//以下三种访问权限
public:
int _pur;
protected:
int _pro;
private:
int _pri;
};
class D:public B//可访问基类的public和protected成员
//class D:protected B//只可访问基类的protected成员
{
public:
void Show()
{
_pur = 0;
_pro = 1;
//_pri = 3;//私有的无法访问
}
};
void FunTest()
{
D d;
d.FunTest1();
B b;
b._pur;
//b._pro;//类外基类的protected成员无法访问
}
int main()
{
FunTest();
return 0;
}
基类的private成员在派生类中是不能被访问的,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。上面已经给出例子。
<2>
1、public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类对象也都是一个父类对象。
2. protetced/private继承是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,是 has-a 的关系原则,所以非特殊情况下不会使用这两种继承关系,在绝大多数的场景下使用的都是公有继承。私有继承以为这is-implemented-in-terms-of(是根据……实现的)。通常比组合(composition)更低级,但当一个派生类需要访问基类保护成员或需要重定义基类的虚函数时它就是合理的。
3. 不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,基类的私有成员存在但是在子类中不可见(不能访问)。
4.在实际运用中一般使用都是public继承,极少场景下才会使用protetced/private继承.
<3>使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
class B
{
public:
void FunTest1()//函数
{
cout<<"FunTest"<<endl;
}
//以下三种访问权限
public:
int _pur;
protected:
int _pro;
private:
int _pri;
};
class D:/*public*/ B//使用关键字class默认的继承方式是private
//struct D:B//使用关键字struct默认的继承方式是public
{
public:
void Show()
{
_pur = 0;
_pro = 1;
//_pri = 3;//私有的无法访问
}
};
void FunTest()
{
D d;
d.FunTest1();
}
int main()
{
FunTest();
return 0;
二、派生类的默认成员函数
在继承关系里,在派生类中如果没有显示定义这六个成员函数,编译系统则会默认合成这六个默认的成员函数
1、继承关心中构造函数调用顺序
<1>基类没有缺省构造函数,派生类必须要在初始化列表中显示给出基类名和参数列表
<2>基类没有定义构造函数,则派生类也可以不用定义,全部使用缺省构造函数。
<3>基类定义了带有形参表的构造函数,派生类就一定定义构造函数。
2、继承关系中析构函数调用过程
下面的例子都可以体现出来
class Base//带参数的继承
{
public:
Base(int data)//非缺省构造函数
//Base(int data = 0)//缺省构造函数
{
cout<<"Base"<<endl;
}
~Base()
{
cout<<"~Base"<<endl;
}
};
class Derived:public Base
{
public:
//Derived() //缺省构造函数
Derived(int data)//基类没有缺省构造函数,派生类必须要在初始化列表中显式给出基类名和参数列表。
:Base(data)
{
cout<<"Derived"<<endl;
}
~Derived()
{
cout<<"~Derived"<<endl;
}
};
void FunTest()
{
//Derived d();//对应的是//Base(int data = 0)
Derived d(1);//带参数
}
int main()
{
FunTest();
return 0;
}
三、继承体系中的作用域
1. 在继承体系中基类和派生类是两个不同作用域。2. 子类和父类中有同名成员,子类成员将屏蔽父类对成员的直接访问。(在子类成员函数中,可以使用 基类::基类成员 访问)--隐藏 --重定义
3. 注意在实际中在继承体系里面最好不要定义同名的成员。
四、继承与转换--赋值兼容规则--public继承
1. 子类对象可以赋值给父类对象(切割/切片)2. 父类对象不能赋值给子类对象
3. 父类的指针/引用可以指向子类对象
4. 子类的指针/引用不能指向父类对象(可以通过强制类型转换完成)
class B
{
public:
B()
{
cout<<"B()"<<endl;
}
~B()
{
cout<<"~B()"<<endl;
}
int data;
};
class D:public B
{
public:
D()
{
cout<<"D()"<<endl;
}
~D()
{
cout<<"~D()"<<endl;
}
};
void FunTest()
{
B b;
B* pb = &b;
D d;
D* pd= &d;
b = d;//子类对象可以赋值给父类对象(切割/切片)
pb = &d;//父类的指针/引用可以指向子类对象
}
int main()
{
FunTest();
return 0;
}
五、友元与继承
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员。
六、继承与静态成员
基类定义了static成员,则整个继承体系里面只有一个这样的成员,无论派生出多少个子类,都只有一个static成员实例。class Base
{
public:
Base()
{
++count;
}
public:
static int count;
};
class Derived:public Base
{
protected:
int Num1;
};
void FunTest()
{
Derived d1;
Derived d2;
cout<<Base::count<<endl;
Derived::count = 0;
cout<<Base::count<<endl;
}
int Base::count = 0;
int main()
{
FunTest();
return 0;
}
七、单继承&多继承&菱形继承
1、单继承前面已经提过,在这就不多介绍。
顺便测试了派生类析构函数
class Base1
{
public:
Base1(int Num)
{
cout<<"Base1"<<endl;
}
~Base1()
{
cout<<"~Base1"<<endl;
}
};
class Base2
{
public:
Base2(int Sur)
{
cout<<"Base2"<<endl;
}
~Base2()
{
cout<<"~Base2"<<endl;
}
};
class Derived: public Base1,public Base2
{
public:
Derived(int Num,int Sur)
:Base1(Num)
,Number1(Num)
,Base2(Sur)
,Number2(Sur)
{
cout<<"Derived"<<endl;
}
~Derived()
{
cout<<"~Derived"<<endl;
}
private:
Base1 Number1;
Base2 Number2;
};
void FunTest()
{
Derived d(1,2);
}
int main()
{
FunTest();
return 0;
}
3、菱形继承
存在问题:菱形继承存在二义性和数据冗余的问题
class Base//定义基类
{
public:
Base()
{
cout<<"Base"<<endl;
}
int a;
};
class Base1:public Base//定义派生类Base1
{
public://新增外部接口
int b;
};
class Base2:public Base//定义派生类Base2
{
public://新增外部接口
int c;
};
class Derived:public Base1,public Base2//定义派生类Derived
{
public://新增外部接口
void Fun()
{
cout<<"Derived"<<endl;
}
int S;
};
void FunTest()
{
Derived d;//创建派生类对象
d.Base1::a = 1;//使用直接基类
d.Base2::a = 2;
}
int main()
{
FunTest();
return 0;
}
八、虚继承
虚继承--解决菱形继承的二义性和数据冗余的问题
1. 虚继承解决了在菱形继承体系里面子类对象包含多份父类对象的数据冗余&浪费空间的问题。
2. 虚继承体系看起来好复杂,在实际应用我们通常不会定义如此复杂的继承体系。一般不到万不得已都不要定义菱形结构的虚继承体系结构,因为使用虚继承解决数据冗余问题也带来了性能上的损耗。
class Base//定义基类
{
public:
Base()
{
cout<<"Base"<<endl;
}
int a;
};
class Base1:virtual public Base//定义虚派生类Base1
{
public://新增外部接口
Base1()
{
cout<<"Base1"<<endl;
}
int b;
};
class Base2: virtual public Base//定义虚派生类Base2
{
public://新增外部接口
Base2()
{
cout<<"Base2"<<endl;
}
int c;
};
class Derived:public Base1,public Base2//定义派生类Derived
{
public://新增外部接口
void Fun()
{
cout<<"Derived"<<endl;
}
int S;
};
void FunTest()
{
Derived d;//创建派生类对象
d.b;
}
int main()
{
FunTest();
return 0;
}
虚继承不是很了解希望大神能给与指导,谢谢!!!