继承
单继承
多继承
(本章节中例子都是用 VS2005 编译调试的)
方式:
三种继承方式的对比:
特征 公有继承 保护继承 私有继承
公有成员继成 派生类公有成员 派生类保护成员 派生类私有成员
私有成员继成 只能通过基类接口访问 只能通过基类接口访问 只能通过基类接口访问
保护成员继成 派生类保护成员 派生类保护成员 派生类私有成员
能否隐式转换 是 是(但只能在此派生类中) 否
在没有虚函数的情况下,类指针调用函数是注意:
虚函数表:
定义:
为了达到动态绑定(后期绑定)的目的,C++编译器通过某个表格,在执行期"间接"调用实际上欲绑定的函数(注意"间接"字眼).这样的表格称为虚函数表(常被称为vtable).每一个"内含虚函数的类",编译器都会为它做一个虚函数表,表中的每一个元素都指向一个虚函数的地址.此外,编译器当然会为这个类加上一项成员变量,是一个指向这个虚函数表的指针(常被称为vptr),且每个由此派生出来的对象,都会有这个一个vptr.
介绍:
当我们通过这个对象调用虚函数时,事实上是通过vptr找到虚函数,再找出真实的地址,虚函数表用这种间接的的方式,虚函数表的内容是依据类中的虚函数声明次序,意义填入函数表(以及所有其他可以继承的成员),当我们在派生类中改写虚函数时,虚函数表就受了影响:表中元素所指的函数地址将不再是基类的函数地址,而是派生类的地址
参考链接: [什么是虚函数] [虚函数表解析]
例子:
三个继承权限的区别:
1 class ex0 2 { 3 private: 4 void showPrivate(){cout<<"this is private function!";} 5 public: 6 void showPublic(){cout<<"this is public function!";} 7 protected: 8 void showProtected(){cout<<"this is protected function!";} 9 }; 10 class ex1:public ex0 11 { 12 public: 13 void func() 14 { 15 showPrivate(); 16 //错误因为此函数访问权限只有基类 ex0 自己有 17 showPublic(); 18 showProtected(); 19 } 20 }; 21 class ex2:protected ex0 22 { 23 public: 24 void func() 25 { 26 showPrivate(); 27 //错误因为此函数访问权限只有基类 ex0 自己有 28 showPublic(); 29 //正确, 但是此函数由于 ex2 的保护继承这个函数的访问权限已经变成了 protected, 30 //也就是说对于外部类来说已经不具备访问这个函数的权限了 31 showProtected(); 32 } 33 }; 34 class ex3:private ex0 35 { 36 public: 37 void func() 38 { 39 showPrivate(); 40 //错误因为此函数访问权限只有基类 ex0 自己有 41 showPublic(); 42 //正确, 但是此函数由于 ex3 的私有继承这个函数的访问权限已经变成了 private, 43 //也就是说对于外部类和派生类来说已经不具备访问这个函数的权限了 44 showProtected(); 45 //正确, 但是此函数由于 ex3 的私有继承这个函数的访问权限已经变成了 private, 46 //也就是说对于外部类和派生类来说已经不具备访问这个函数的权限了 47 } 48 };
有没有虚函数的情况下,类指针调用函数的区别:
[此处例子链接]
[返回目录]
声明格式:
class 派生类名:继承方式(若不具体指出默认为private) 基类名
{
…
};
特性:
继承了基类所有属性与行为,包括私有成员,但不允许派生类直接访问基类私有成员
构造函数:
格式: 派生类构造函数名(形参表):基类构造函数名(形参表){…}
要点:
● 创建派生类对象时,程序首先创建基类对象,即基类对象应在进入派生类构造函数前被创建完成
(即先调用基类构造函数,后调用派生类构造函数)
● 派生类构造函数应通过成员初始化表将基类信息传递给基类构造函数
● 派生类构造函数应初始化派生类新增的数据成员
析构函数:
特性: 派生类对象过期时,程序将首先调用派生类析构函数,然后调用基类的
虚析构函数:
作用: 和虚函数一样类似,在用基类指针释放派生类对象时候,为了能调用正确的析构函数.
注意: 当一个类有虚函数功能,它经常作为一个基类使用,并且它的派生类经常使用new来分配,那么它最好也使用虚析构函数,因为这样才能保证在释放实例对象的时候调用正确的析构函数
注意:
例子:
构造函数和析构函数的调用顺序:
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 class A{ 6 public: 7 A(){cout<<"A::A() ";} 8 ~A(){cout<<"A::~A()"<<endl;} 9 }; 10 class B:virtual public A{ 11 public: 12 B(){cout<<"B::B() "<<endl;} 13 ~B(){cout<<"B::~B() ";} 14 }; 15 void func() 16 { 17 cout<<"this is class D:"<<endl; 18 cout<<"the order of constructor: "; 19 B b; 20 cout<<"the order of destructor: "; 21 } 22 23 void main() 24 { 25 func(); 26 system("pause"); 27 } 28 /******************************** 29 输出结果: 30 this is class D: 31 the order of constructor: A::A() B::B() 32 the order of destructor: B::~B() A::~A() 33 请按任意键继续. . . 34 ********************************/
虚函数使用:
[此处为链接]
无虚析构函数:
1 class A{ 2 public: 3 A(){cout<<"A::A() ";} 4 ~A(){cout<<"A::~A()"<<endl;} 5 }; 6 class B:public A{ 7 public: 8 B(){cout<<"B::B() "<<endl;} 9 ~B(){cout<<"B::~B() ";} 10 }; 11 void func() 12 { 13 A* pb = new B(); 14 delete pb; 15 } 16 /****************************************** 17 调用 func 函数的输出结果: 18 A::A() B::B() 19 A::~A() 20 ******************************************/
有虚析构函数:
1 class A{ 2 public: 3 A(){cout<<"A::A() ";} 4 virtual ~A(){cout<<"A::~A()"<<endl;} 5 }; 6 class B:public A{ 7 public: 8 B(){cout<<"B::B() "<<endl;} 9 ~B(){cout<<"B::~B() ";} 10 }; 11 void func() 12 { 13 A* pb = new B(); 14 delete pb; 15 } 16 /***************************************** 17 调用 func 函数的输出结果: 18 A::A() B::B() 19 B::~B() A::~A() 20 *****************************************/
基类构造函数的调用:
1 class A{ 2 public: 3 A(){cout<<"this is A::A()"<<endl;} 4 A(int b){cout<<"this is A::A(int b): the value of b is: "<<b<<endl;} 5 A(double b){cout<<"this is A::A(double b) the value of b is: "<<b<<endl;} 6 }; 7 class B:public A{ 8 public: 9 B():A(){} //调用基类 A 默认构造函数 10 B(int b):A(b){} //调用基类 A 的 A::A(int b) 构造函数 11 B(double b):A(b){} //调用基类 A 的 A::A(double b) 构造函数 12 }; 13 void main() 14 { 15 B b0,b1(5),b2(10.5);//产生三个实例,分别调用不同的基类构造函数 16 system("pause"); 17 } 18 /*********************************** 19 调用 func 函数的输出结果: 20 this is A::A() 21 this is A::A(int b): the value of b is: 5 22 this is A::A(double b) the value of b is: 10.5 23 ***********************************/
[返回目录]
声明格式:
class 派生类名:继承方式 基类名,继承方式 基类名… {….};
构造函数:
派生类名::派生类名(形参表):基类名(形参表),基类名(形参表)…{…}
处理同一层的基类构造函数的执行顺序取决于定义派生类对各基类的排列顺序,与定义派生类的构造函数时基类的排列顺序无关
原因:
方法:
虚基类:
声明格式: class 派生类名:virtual 继承方式 基类名,… { … };
注意:
有虚基类和无虚基类的区别:
例子:
没有虚基类继承:
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 class A{ 6 protected: 7 int a; 8 public: 9 A():a(0){}; //对成员变量 a 进行初始化 10 }; 11 class B:public A{ 12 public: 13 void B_changeValue(int b) //在派生类 B 中修改基类 A 的成员变量 a 并输出其前后的值 14 { 15 cout<<"in the class B"<<endl; 16 cout<<"the original value of a is: "<<a<<endl; 17 a = b; 18 cout<<"the value after change is: "<<a<<endl<<endl; 19 } 20 }; 21 class C:public A{ 22 public: 23 void C_changeValue(int c) //在派生类 C 中修改基类 A 的成员变量 a 并输出其前后的值 24 { 25 cout<<"in the class C"<<endl; 26 cout<<"the original value of a is: "<<a<<endl; 27 a = c; 28 cout<<"the value after change is: "<<a<<endl<<endl; 29 } 30 }; 31 class D:public B,public C{ 32 33 }; 34 35 void main() 36 { 37 D d; 38 d.B_changeValue(10); 39 d.C_changeValue(20); 40 d.B_changeValue(11); 41 d.C_changeValue(21); 42 system("pause"); 43 }
输出结果:
由此可以看出,其实派生类 D 从基类 B 与基类 C 中继承了两份 A 的数据变量备份
有虚基类的继承:
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 class A{ 6 protected: 7 int a; 8 public: 9 A():a(0){}; //初始化成员变量 a 10 }; 11 class B:virtual public A{ 12 public: 13 void B_changeValue(int b) // 在派生 B 中修改基类 A 的成员变量 a 并输出其修改前后的值 14 { 15 cout<<"in the class B"<<endl; 16 cout<<"the original value of a is: "<<a<<endl; 17 a = b; 18 cout<<"the value after change is: "<<a<<endl<<endl; 19 } 20 }; 21 class C:virtual public A{ 22 public: 23 void C_changeValue(int c) // 在派生 C 中修改基类 A 的成员变量 a 并输出其修改前后的值 24 { 25 cout<<"in the class C"<<endl; 26 cout<<"the original value of a is: "<<a<<endl; 27 a = c; 28 cout<<"the value after change is: "<<a<<endl<<endl; 29 } 30 }; 31 class D:public B,public C{ 32 33 }; 34 35 void main() 36 { 37 D d; 38 d.B_changeValue(10); 39 d.C_changeValue(20); 40 d.B_changeValue(11); 41 d.C_changeValue(21); 42 system("pause"); 43 }
输出结果:
由此可以看出,这次派生类 D 从基类 B 与基类 C 中继承了只有一份 A 的数据变量备份
构造函数与析构函数的顺序:
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 class A{ 6 public: 7 A(){cout<<"A::A() ";} 8 ~A(){cout<<"A::~A()"<<endl;} 9 }; 10 class B:virtual public A{ 11 public: 12 B(){cout<<"B::B() ";} 13 ~B(){cout<<"B::~B() ";} 14 }; 15 class C:virtual public A{ 16 public: 17 C(){cout<<"C::C() ";} 18 ~C(){cout<<"C::~C() ";} 19 }; 20 class D:public B,public C{ 21 public: 22 D():C(),B(){cout<<"D::D()"<<endl;} 23 ~D(){cout<<"D::~D() ";} 24 }; 25 class E:public C,public B 26 { 27 public: 28 E():C(),B(){cout<<"E::E()"<<endl;} 29 ~E(){cout<<"E::~E() ";} 30 }; 31 void funcD() 32 { 33 cout<<"this is class D:"<<endl; 34 cout<<"the order of constructor: "; 35 D d; 36 cout<<"the order of destructor: "; 37 } 38 void funcE() 39 { 40 cout<<"this is class E:"<<endl; 41 cout<<"the order of constructor: "; 42 E e; 43 cout<<"the order of destructor: "; 44 } 45 void main() 46 { 47 funcD(); 48 cout<<endl; 49 funcE(); 50 cout<<endl; 51 system("pause"); 52 }
输出结构:
由此可以看出,构造函数的顺序与继承顺序有关,而不是基类构造函数的调用有关
基类构造函数调用:
1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 class A{ 6 public: 7 A(){cout<<"this is A::A()"<<endl;} 8 A(int b){cout<<"this is A::A(int b): the value of b is: "<<b<<endl;} 9 A(double b){cout<<"this is A::A(double b) the value of b is: "<<b<<endl;} 10 }; 11 class B 12 { 13 public: 14 B(){cout<<"this is B::B()"<<endl;} 15 B(int b){cout<<"this is B::B(int b): the value of b is: "<<b<<endl;} 16 B(double b){cout<<"this is B::B(double b) the value of b is: "<<b<<endl;} 17 }; 18 class C:public A,public B{ 19 public: 20 C():A(),B(){cout<<endl;} 21 C(int b,double c):A(b),B(c){cout<<endl;} 22 C(double b,int c):A(b),B(c){cout<<endl;} 23 }; 24 void main() 25 { 26 C c0,c1(5,15.123),c2(10.5,1235); 27 system("pause"); 28 }
输出结果:
[返回目录]