目录
一、继承
1.继承访问权限测试
2.友元类继承测试
3.总结
二、多态
1.多态性
2.一般多态性函数
3.特殊多态性函数
4.析构函数的多态性
5.多继承
6.总结
一、继承
1.继承访问权限测试
类A:
class A{
public:
int _a=1;
protected:
int _b=2;
private:
int _c=3;
};
类B1以public方式继承A:
class B1:public A{
public:
void Test(){
_a=10;
_b=10;
//_c=10;//'_c' is a private member of 'A'
}
int B1_pub_b1=11;
protected:
int B1_pro_b2=12;
private:
int B1_pri_b3=13;
};
类B2以protected方式继承B:
class B2:protected A{
public:
void Test(){
_a=10;
_b=10;
//_c=10;//'_c' is a private member of 'A'
}
int B2_pub_b1=21;
protected:
int B2_pro_b2=22;
private:
int B2_pri_b3=23;
};
类B3以private方式继承A:
class B3:private A{
public:
void Test(){
_a=10;
_b=10;
//_c=10;//'_c' is a private member of 'A'
}
int B3_pub_b1=31;
protected:
int B3_pro_b2=32;
private:
int B3_pri_b3=33;
};
类newB3以private方式继承A,并把A中的部分public成员提升为public:
class newB3:private A{
public:
void Test(){
_a=10;
_b=10;
//_c=10;//'_c' is a private member of 'A'
}
int B3_pub_b1=31;
using A::_a;//用{using A::_a; }把A中的部分public成员提升为public
protected:
int B3_pro_b2=32;
private:
int B3_pri_b3=33;
};
测试结果:
int main(){
A a;
B1 t1;
B2 t2;
B3 t3;
newB3 t4;
cout<<"A: ";
cout<
2.友元类继承测试
类A:
class A{
private:
int a;
friend class C;
};
类B:
class B: public A{
private:
int b;
};
类C:
class C{
public:
void Test(){
B b1;
b1.a;
//b1.b;//b is private member of B
}
};
类D:
class D:public C{
public:
void Test(){
A a1;
//a1.a;//a is a private member of A
B b2;
//b2.a;//a is a private member of A
//b2.b;//b is private member of B
}
};
3.总结
public继承的访问控制:
1、基类的public和protected成员:访问属性在派生类中保持不变;
2、基类的private成员:不可直接访问。
访问权限:
1、派生类的成员函数,可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;
2、通过派生类的对象,只能访问public成员。
private继承的访问控制:
1、基类的public和protected成员,都以private身份出现在派生类中;
2、基类的private成员:不可直接访问。
访问权限:
1、派生类的成员函数,可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;(调用基类的函数时,可用base::test().)
2、通过派生类的对象,不能访问从基类继承的任何成员。
protected继承的访问控制:
1、基类的public和protected成员:都以protected身份出现在派生类中;
2、基类的private成员:不可直接访问。
访问权限:
1、派生类的成员函数,可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员;
2、通过派生类的对象,不能访问从基类继承的任何成员。
通常,类的私有成员只能由本类的成员访问,外部函数只能访问类的成员函数,再由成员函数访问类的私有成员。但是,如果在某个类定义中用friend声明了一个外部函数(也许是其他类的一个成员)后,这个外部函数便可以例外地访问该类的任何私有成员。用friend声明了的外部函数称为这个类的友元函数。友元关系是单向的,不具有交换性。友元关系也不具有传递性。友元关系不能被继承,基类的友元类未必是子类的友元,某类型的友元的子类未必是该类型的友元。
二、多态
1.多态性
在面向对象方法中,所谓多态性就是不同对象收到相同消息,产生不同的行为。在C++程序设计中,多态性是指用一个名字定义不同的函数,这些函数执行不同但又类似的操作,这样就可以用同一个函数名调用不同内容的函数。换言之,可以用同样的接口访问功能不同的函数,从而实现“一个接口,多种方法”。
在C++中,多态性的实现和联编(也称绑定)这一概念有关。一个源程序经过编译、链接,成为可执行文件的过程是把可执行代码联编(或称装配)在一起的过程。其中在运行之前就完成的联编成为静态联编(前期联编);而在程序运行之时才完成的联编叫动态联编(后期联编)。
静态联编支持的多态性称为编译时多态性(静态多态性)。在C++中,编译时多态性是通过函数重载和模板实现的。利用函数重载机制,在调用同名函数时,编译系统会根据实参的具体情况确定索要调用的是哪个函数。
动态联编所支持的多态性称为运行时多态(动态多态)。在C++中,运行时多态性是通过虚函数来实现的。
2.一般多态性函数
输入输出参数完全一样,在父类中添加virtual;
#include#include using namespace std; class Person { public: virtual void BuyTicket() { cout << "买票:全价" << endl; } }; class Student : public Person { public: void BuyTicket(){ cout << "买票:半价" << endl; } }; class Old:public Person { public: void BuyTicket() { cout<<"买票:半价"< BuyTicket(); } system("pause"); return 0; }
在派生类中重写的成员函数可以不加virtual关键字,也是构成重写,因为继承后基类的虚函数被继承下来,在派生类中依旧保持虚函数的属性,我们只是重写了它。这是非常不规范的,在平时尽量不要这样使用。
若子类中的函数有virtual修饰,而父类中没有,则会构成函数隐藏。基类中的析构函数如果是虚函数,那么派生类的析构函数就重写了基类的析构函数。这里他们的函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor,这也说明基类的析构函数最好写成虚函数。
3.特殊多态性函数
输入或输出参数在子类中是父类的指针或基类的引用,在子类中对于的是子类的指针或子类的引用。
void getBuy(Person* P){
P->BuyTicket();
}
int main()
{
Person per;
Student stu;
Old old;
getBuy(&per);
getBuy(&stu);
getBuy(&old);
return 0;
}
4.析构函数的多态性
在应用C++的多态特性时,常常会碰到一种情况,就是当指向基类的指针被释放时,派生类的析构函数其实没有被调用,导致在派生类中申请的空间没有被释放,导致内存泄漏。
#includeusing namespace std; class Mammal { public: Mammal() {cout << "Mammal constructor... n";} // virtual ~Mammal() {cout << "Mammal destructor... n";} ~Mammal() {cout << "Mammal destructor... n";} virtual void Speak() const {cout << "Mammal speak!n";} }; class Dog:public Mammal { public: Dog() { p = new int(0); cout << "Dog Constructor...n"; } ~Dog() { cout << "Dog destructor...n"; delete p; } void Speak() const {cout << "Woof!n";} private: int *p; }; int main() { Mammal* pDog = new Dog; pDog->Speak(); delete pDog; return 0; }
派生类Dog的析构函数没有调用,Dog的构造函数申请的空间泄漏了。此时,虚析构函数能够有效的防止这种情况的出现。
class Mammal {
public:
Mammal() {cout << "Mammal constructor... n";}
virtual ~Mammal() {cout << "Mammal destructor... n";}
// ~Mammal() {cout << "Mammal destructor... n";}
virtual void Speak() const {cout << "Mammal speak!n";}
};
通过将基类的析构函数声明为虚析构函数,成功的通过基类指针调用了派生类的析构函数,完成了内存的释放。
5.多继承
多继承的概念: 如果一个派生类从多个基类继承, 则称为多继承。
多继承的声明:
class 派生类名:访问控制 基类名1, 访问控制 基类名2, …
{
成员列表
}
虚继承:
#includeusing namespace std; class base { public: int base; virtual void say() { cout << "I am base" << endl; } }; class A : public base { public: int a; }; class B : public base { public: int b; }; class C :public A, public B { public: int c; }; int main() { C c; c.say(); return 0; }
此时出现二义性,可以使用虚继承
class A : virtual public base
{
public:
int a;
};
class B : virtual public base
{
public:
int b;
};
6.总结
虚函数的定义要遵循以下重要规则:
- 如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行滞后联编的。
- 只有类的成员函数才能说明为虚函数,因为虚函数仅适合用与有继承关系的类对象,所以普通函数不能说明为虚函数。
- 静态成员函数不能是虚函数,因为静态成员函数的特点是不受限制于某个对象。
- 内联(inline)函数不能是虚函数,因为内联函数不能在运行中动态确定位置。即使虚函数在类的内部定义定义,但是在编译的时候系统仍然将它看做是非内联的。
- 构造函数不能是虚函数,因为构造的时候,对象还是一片位定型的空间,只有构造完成后,对象才是具体类的实例。
- 析构函数可以是虚函数,而且通常声名为虚函数。
多态的特性的virtual修饰,不单单对基类和派生类的普通成员 函数有必要,而且对于基类和派生类的析构函数同样重要。



