目录
一:继承访问权限
二:友元类继承
三:多态性综合运用
3.1一般性多态
3.2特殊多态性函数
3.3析构函数的多态性
3.4多继承
继承是类的重要特性。A类继承B类,我们称B类为“基类”,A为“子类”,继承有三种方式,分别为公有继承方式(public),保护继承(protected)和私有继承(private)。
在公有继承方式中基类的私有成员,子类不可以访问;基类的保护成员,子类可以继承为自己的保护成员,在外部不可以访问;基类的公有成员,子类可以继承为自己的公有成员,在外部也可以访问。
在保护继承方式中基类公有成员,子类中继承为自己的保护成员,在外部不可以访问;基类保护成员,子类中继承为自己的保护成员,在外部不可以访问;基类私有成员,子类一样不可以访问基类的私有成员。
在私有继承方式中基类公有成员,子类中继承为自己的私有成员,在外部不可以访问;基类保护成员,子类中继承为自己的私有成员,在外部不可以访问;基类私有成员,子类一样不可以访问基类的私有成员。
下图也清晰的表达了继承的各种关系
现在来进行测试:
首先是A具有public, protected, private等变量
class A { public: int A_1; protected: int A_2; private: int A_3; };
然后是B1,B2,B3分别以public, protected, private方式继承A
class B1: public A { public: void test() { A_1 = 10; A_2 = 10; //A_3 = 10; 无法访问 } int B1_1; protected: int B1_2; private: int B1_3; }; class B2: protected A { public: void test() { A_1 = 10; A_2 = 10; //A_3 = 10; 无法访问 } int B2_1; protected: int B2_2; private: int B2_3; }; class B3 : private A { public: using A::A_1; void test() { A_1 = 10; A_2 = 10; //A_3 = 10; 无法访问 } int B3_1; protected: int B3_2; private: int B3_3; };
在上面代码可以发现对于子类无法访问基类的私有变量。接下来看在外部的访问情况,讲解会在代码的注释上面
void test1() { A a; a.A_1=10; //a.A_2; a.A_3 A类的protected和private无法访问 B1 b1; b1.B1_1 = 10; //b1.B1_2; b1.B1_3; B1类的protected和private无法访问 b1.A_1 = 10; //b1.A_2; b1.A_3; A类的protected和private无法访问 B2 b2; b2.B2_1 = 10; //b2.B2_2; b2.B2_3; B2类的protected和private无法访问 //b2.A_1; b2.A_2; b2.A_3; A类的变量全部无法访问 B3 b3; b3.B3_1 = 10; //b3.B3_2; b3.B3_3; B3类的protected和private无法访问 //b3.A_1; b3.A_2; b3.A_3; A类的变量全部无法访问 b3.A_1; //当采用using的方式就可以将private方式继承的public成员改成public }
在定义一个类的时候,可以把一些函数声明为“友元”,这样那些函数就成为该类的友元函数,在友元函数内部就可以访问该类对象的私有成员了。代码实验如下
#include<istream> using namespace std; class A { private: int a; friend class C; }; class B: public A { private: int b; }; class C { public: void test() { B b1; b1.a; //b1.b; 无法访问b中的私有变量 } }; class D: public C { public: void test() { A A1; //A1.a; 无法访问A中私有变量 B b2; //b2.a; 无法访问B继承A的私有变量 //b2.b; 无法访问B中的私有变量 } };
如上面的代码类A中友元给类C,B继承A,a,b分别为类A,B中的使用变量。类C中测试访问类B的成员变量a, b,结果可以访问a,但不能访问b。D继承C,在D的成员函数中测试访问类A的成员变量a,类B的成员变量a, b,结果全部无法访问。因为A仅仅友元了C,虽然D继承的C,但不能继承C的友元。 朋友的儿子不可访问protected及以下级别,但可以访问从父类继承过来的protected及以下级别。
#include<iostream> using namespace std; class A{ public: void f1() { cout << "A::f1" << endl; } virtual void f2() { cout << "A::f2" << endl; } virtual void f3() = 0; }; class B:public A { public: void f1() { cout << "B::f1" << endl; } void f2() { cout << "B::f2" << endl; } void f3() { cout << "B::f3" << endl; } }; int main(){ A * a = new B(); a->f1(); //普通函数会调用基函数,即A类的函数 a->f2(); //多态,会调用子类重写基类的方法 a->f3(); //多态,调用子类对纯虚函数的实现 delete a; return 0; }
我们先来看一下代码的结果在来分析
对于第一个调用,f1()在A中并没有添加virtual,因此调用的函数为A中的函数,后两个加上virtual后,因为创建的时候为B类,因此会调用B中的函数,其中f2()为子类的重写,f3()为子类对虚函数的实现。
输入或输出参数在子类中是父类的指针或基类的引用,在子类中对于的是子类的指针或子类的引用;
using namespace std; class A{ public: virtual void f() { cout << "A::f" << endl; } }; class B:public A { public: void f() { cout << "B::f" << endl; } }; class C:public A { public: void f() { cout << "C::f" << endl; } }; void out1(A & X){ X.f(); }; void out2(A * X){ X->f(); }; int main() { A A1 ; out1(A1); out2(&A1); B B1; out1(B1); out2(&B1); C C1; out1(C1); out2(&C1); }
结果为:
在调用的过程中出现了父类引用指向子类对象,这种情况是可以的,因为子类可能含有一些父类没有的成员变量或者方法函数,但是子类肯定继承了父类所有的成员变量和方法函数,所以用父类指针指向子类时,没有问题。
为了防止内存的泄露,往往需要写析构函数,而析构函数和虚析构函数又有不同的地方,前者只会调用基类的析构函数,而后者会同时调用子类的析构函数。下面看代码来简单了解一下两种情况
#include<iostream> using namespace std; class A{ public: A() { cout << "A::A()" << endl; } ~A() { cout << "A::~A()" << endl; }; }; class B:public A { public: B() { cout << "B::B()" << endl; } ~B() { cout << "B::~B()" << endl; }; }; int main(){ A * a = new B(); delete a; //此时只会调用A的析构函数,不会调用B的析构函数,而事实上也调用了B的构造函数 return 0; }
结果展示:
这样B创建了但是在最后没有解放,而采用虚析构函数就可以同时解放子类
#include<iostream> using namespace std; class A{ public: A() { cout << "A::A()" << endl; } virtual ~A() { cout << "A::~A()" << endl; }; }; class B:public A { public: B() { cout << "B::B()" << endl; } virtual ~B() { cout << "B::~B()" << endl; }; }; int main(){ A * a = new B(); delete a; //此时只会调用A的析构函数,不会调用B的析构函数,而事实上也调用了B的构造函数 return 0; }
结果展示:
一个类有多个基类,那么这种继承关系就叫做多继承。多继承是指派生类有多个基类,派生类与每个基类之间的关系也看作是一个单继承。
class A{ protected: int a; }; class B: virtual public A{ //虚继承 protected: int b; }; class C: virtual public A{ //虚继承 protected: int c; }; class D: public B, public C{ private: int d; };
上面D继承了B和C就是多继承的一个简单例子,多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序。