所谓虚函数是指:html
在类中被声明为virtual的成员,基类但愿这种成员在派生类中重定义。除了构造函数外,任意非static成员均可觉得虚成员。保留字 virtual 只在类内部的成员函数声明中出现,不能用在类定义体外部出如今函数定义上。ios
protected标号:less
protected为受保护的访问标号,protected成员能够被该类的成员、友元和派生类成员(非友元)访问,而不能够被该类型的普通用户访问。函数
而private成员,只能被基类的成员和友元访问,派生类不能访问。布局
派生类只能经过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊访问权限。post
派生类中虚函数的声明必须与基类中的定义方式彻底匹配,但有一个例外:返回对基类型的引用(或指针)的虚函数。派生类中的虚函数能够返回基类函数所返回类型的派生类的引用(或指针),或返回基类类型的引用(或指针);this
一旦函数在基类中声明为虚函数,它就一直为虚函数,派生类没法改变该函数为虚函数的这一事实。派生类重定义虚函数时,可使用 virtual 保留字,可是不是必须这样作。url
已定义的类才能够作基类。若是已经声明了一个类,可是没有定义它,则不能用做基类。spa
若是需声明(但不实现)一个派生类,则声明包含类名但不包含派生列表。例如,下边的前向声明会致使编译时错误:3d
//error:a forward declaration must not include the derivation list class Bulk_item:public Item_base;
正确的前向声明为:
//forward declarations of both derived and nonderived class class Bulk_item; class Item_base;
C++中的函数调用默认不使用动态绑定。要触发动态绑定,必须知足两个条件:
第一:只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不进行动态绑定;
第二:必须经过基类类型的引用或指针进行函数调用。
派生类虚函数调用基类版本时,必须显示使用做用与操做符。若是派生类函数忽略了这样作,则函数调用会在运行时肯定而且将是一个自身调用,从而致使无穷递 归。(只有成员函数中的代码才应该使用做用域操做符覆盖虚函数机制)例如:
1 // Bulk_item 是 Item_base 的派生类 2 Bulk_item derived; 3 Item_base *baseP = &derived; 4 //calls version from the base class regardless of the dynamic type of baseP 5 double d = baseP->Item_base::net_price(42); //显示调用做用域操做符
注意:虚函数也能够有默认实参。一般,若是有用在给定调用中的默认实参值,该值将在 编译(注定不是动态绑定) 时肯定。若是调用省略了具备默认值的实参,则所用的值由调用该函数的类型定义,与对象的动态类型无关。经过基类的引用或指针调用虚函数时,默认实参为在基类虚函数声明中指定的值,若是经过派生类的指针或引用调用虚函数,则默认实参是在派生类的版本中声明的值。在同一虚函数的基类版本和派生类版本中使用不一样的默认实参几乎必定会引发麻烦。若是经过基类的引用或指针调用虚函数,但实际执行的是派生类中定义的版本,这时就可能会出现问题。在这种状况下,为虚函数的基类版本定义的默认实参将传给派生类定义的版本,而派生类版本是用不一样的默认实参定义的。
注意理解下边的代码:
1 /* 2 不管派生列表中是什么访问标号,全部继承 Base 的类对 Base 中的成员 3 具备相同的访问。派生访问标号将控制派生类的用户对从Base继承而来的 4 成员的访问: 5 */ 6 class Base 7 { 8 public: 9 void basemem(); 10 protected: 11 int i; 12 // ... 13 }; 14 15 struct Public_derived : public Base 16 { 17 int use_base() 18 { 19 return i; // OK:derived classes can access i 20 } 21 }; 22 23 struct Private_derived : private Base 24 { 25 int use_base() 26 { 27 return i; // OK:derived classes can access i,这里能够访问 28 } 29 }; 30 31 int main() 32 { 33 Base b; 34 Public_derived d1; 35 Private_derived d2; 36 b.basemem(); // ok: basemem is public 37 d1.basemem(); // ok: basemem is public in the derived class 38 //d2.basememe(); // error: basemem is private in the derived class 39 }
从Public——derived 派生的类能够访问来自Base类的 i, 是由于该成员在Public_derived 中仍为 protected 成员。从 Private_derived 派生的类没 有这样的访问,对它们而言,Private_derived 从 Base 继承的全部成员均为 private。
派生类不能访问基类的 private 成员,也不能使本身的用户可以访问那些成员。若是基类成员为 public 或 protected,则派生类列表中使用的访问标号决定该成员在派生类中的访问级别:
一、若是是公有继承,基类成员保持本身的访问级别:基类的public成员为派生类的public成员,基类的protected成员为派生类的protected成员;
二、若是是受保护继承,基类的public和protected成员在派生类中为protected成员;
三、若是是私有继承,基类的全部成员在派生类中为private成员。
在派生类构造函数中使用默认实参:
1 // 不使用折扣策略的基类 2 class Item_base 3 { 4 public: 5 Item_base(const std::string &book = "", double sales_price = 0.0) : isbn(bool), price(sales_price){} 6 7 std::string book() const 8 { 9 return isbn; 10 } 11 12 // 返回特定购书量的总价格 13 // 派生类将重载该函数以应用不一样的折扣策略 14 virtual double net_price(size_t n) const 15 { 16 return n * price; 17 } 18 19 virtual ~Item_base(){} 20 21 private: 22 std::string isbn; 23 protected: 24 double price; 25 }; 26 27 //批量购买折扣类 28 class Bulk_item : public Item_base 29 { 30 public: 31 Bulk_item(const std::string& book = "", double sales_price = 0.0, 32 size_t qty = 0, double disc_rate = 0.0) : 33 Item_base(book, sales_price), min_qty(qty), discount(disc_rate){} 34 35 // 重定义基类版本以实现批量购买折扣策略:若购买量高于下限,则使用折扣价格 36 double net_price(size_t cnt) const 37 { 38 if(cnt >= min_qty) 39 return cnt * (1 - discount) * price; 40 else 41 return cnt * price; 42 } 43 private: 44 size_t min_qty; // 可打折的最小购买量 45 double discount; // 折旧率 46 };
下边是转载的内容:http://www.cnblogs.com/bakari/archive/2012/08/12/2635369.html
虚函数是C++中很是重要的一个概念,它最大的好处是可以触发动态绑定。C++中的函数默认不使用动态绑定,要触发动态绑定,必须知足 两个条件:
第一,只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不进行动态绑定;
第二,必须经过基类类型的指针或引用进行函数的调用。具体理解指针或引用在使用继承层次中某一类型的对象时会发生什么,本文不展开讨论,
这两天主要研习了虚函数的具体应用这一块,而它的应用又很是普遍,学MFC的应该可以感觉到它的强大,要说是总结也不必定可以总结全,本人目前也处在studying中,因此用10个具体的例子来讲明。例子是从难 到易,看到的朋友若是懂前面的能够不用看后面的。每个例子就是一个类,经过类在内存中的布局来形象地分析虚函数到底是如何运做的。图表示可能抽象一点,通常带有V开头的表示一个虚函数表,若是是学过编译原理这门课就很容易看懂,没学过的只要懂虚函数的一些机制,耐着性子也是没问题的。每一个图示都配有相应的代码。能够对照着代码来看。
一、 虚函数继承的复杂例子
二、 菱形继承无虚拟继承的状况
三、 虚拟继承的简单状况
四、 单一普通继承(无虚函数)
五、 单一继承(含虚函数)(虚函数表只有一个)
六、 多重继承(不含虚函数)
七、 多重继承(一个含虚函数,一个不含虚函数)
八、 多重继承(两个都含有虚函数)
九、 纯虚汗继承
十、 private 的虚函数
1、虚函数继承的复杂例子,见下图:
见图:左边是这个类的内存布局,右边是继承图示。 farther类和Uncle类都是虚拟继承,其内部也都有偏移表,但我以为这两个表只是内部隐藏的,不在Son的内存布局中表示出来,本题Son的内存只有32个字节,若是表示出来就不止32个了,可是下面这个地方在内存中显示是00 00 00 00,我猜测是否是GrandFather的偏移地址。
VbtSon(Father) |
Farther~Num |
VbtSon(Uncle) |
Uncle~Num |
Son~Num |
Offset(这个地方??) |
Vftable(GrandFather) |
GrandFather~Num |
例子代码:
1 class GrandFather 2 { 3 public: 4 GrandFather():i_G(5){cout<<"GrandFather() is called!"<<endl;} 5 virtual ~GrandFather(){cout<<"~GrandFather() is called!"<<endl;} 6 public: 7 virtual void Test(){cout<<"GrandFather::Test() is called!"<<endl;} 8 private: 9 int i_G; 10 }; 11 12 class Father: virtual public GrandFather //虚拟继承 13 { 14 public: 15 Father():i_F(7){cout<<"Father() is called!"<<endl;}; 16 virtual ~Father(){cout<<"~Father() is called!"<<endl;} 17 public: 18 virtual void Test(){cout<<"Father::Test() is called!"<<endl;} 19 private: 20 int i_F; 21 }; 22 23 class Uncle: virtual public GrandFather //虚拟继承 24 { 25 public: 26 Uncle():i_U(3){cout<<"Uncle is called!"<<endl;} 27 virtual ~Uncle(){cout<<"~Uncle is called!"<<endl;} 28 public: 29 virtual void Test(){cout<<"Uncle ::Test() is called!"<<endl;} 30 private: 31 int i_U; 32 }; 33 34 class Son:public Father,public Uncle 35 { 36 public: 37 Son():i_S(9){cout<<"Son is called!"<<endl;}; 38 virtual ~Son(){cout<<"~Son is called!"<<endl;} 39 public: 40 virtual void Test(){cout<<"Son ::Test() is called!"<<endl;} 41 private: 42 int i_S; 43 }; 44 45 int main(void) 46 { 47 Son p; 48 p.Test(); 49 cout<<sizeof(Son)<<endl; 50 cout<<sizeof(Father)<<endl; 51 cout<<sizeof(GrandFather)<<endl; 52 return 0; 53 }
运行状况:
二、 菱形继承无虚拟继承的状况
VPTr1(Father) |
GrandFarther~Num |
Father~Num |
VPtr(Uncle) |
GrandFarther~Num |
Uncle~Num |
Son~Num |
1 #include<iostream> 2 using namespace std; 3 class GrandFather 4 { 5 public: 6 GrandFather():i_G(5){cout<<"GrandFather() is called!"<<endl;} 7 virtual ~GrandFather(){cout<<"~GrandFather() is called!"<<endl;} 8 public: 9 virtual void Test(){cout<<"GrandFather::Test() is called!"<<endl;} 10 private: 11 int i_G; 12 }; 13 class Father: public GrandFather //无虚拟继承 14 { 15 public: 16 Father():i_F(7){cout<<"Father() is called!"<<endl;}; 17 virtual ~Father(){cout<<"~Father() is called!"<<endl;} 18 public: 19 virtual void Test(){cout<<"Father::Test() is called!"<<endl;} 20 private: 21 int i_F; 22 }; 23 24 class Uncle: public GrandFather //无虚拟继承 25 { 26 public: 27 Uncle():i_U(3){cout<<"Uncle is called!"<<endl;} 28 virtual ~Uncle(){cout<<"~Uncle is called!"<<endl;} 29 public: 30 virtual void Test(){cout<<"Uncle ::Test() is called!"<<endl;} 31 private: 32 int i_U; 33 }; 34 35 class Son:public Father,public Uncle 36 { 37 public: 38 Son():i_S(9){cout<<"Son is called!"<<endl;}; 39 virtual ~Son(){cout<<"~Son is called!"<<endl;} 40 public: 41 virtual void Test(){cout<<"Son ::Test() is called!"<<endl;} 42 private: 43 int i_S; 44 }; 45 46 int main(void) 47 { 48 Son p; 49 p.Test(); 50 cout<<sizeof(Son)<<endl; 51 cout<<sizeof(Father)<<endl; 52 cout<<sizeof(GrandFather)<<endl; 53 54 return 0; 55 }
运行状况:
三、 虚拟继承的简单状况 见下图:
VPTrD(A) 4 |
Offset(A) 4 |
A~number 4 |
D~number 4 |
VPtr(Base) 4 |
Base~Number
12 + 3cc + 4 = 40 |
1 class Base 2 { 3 public: 4 Base(){strcpy_s(ch_rc,"abcdefg");} //初始化Base()::im 5 public: 6 virtual void Read(){cout<<"Base::Read()is called!"<<endl;} 7 private: 8 char ch_rc[12]; 9 bool ir; 10 int im; 11 }; 12 class A: virtual public Base //虚拟继承 13 { 14 public: 15 A():im_A(5){} //初始化A()::im_A 16 public: 17 virtual void Read(){cout<<"A::Read()is called!"<<endl;} 18 private: 19 int im_A; 20 }; 21 class D:public A 22 { 23 public: 24 D():im_D(3){} 25 public: 26 virtual void Read(){cout<<"D::Read()is called!"<<endl;} 27 private: 28 int im_D; 29 }; 30 int _tmain(int argc, _TCHAR* argv[]) 31 { 32 D obj; 33 cout<<sizeof(D)<<endl; 34 return 0; 35 }
运行状况:
4、单一普通继承(无虚函数)(这个没什么好说的)
内存布局
Father~Number |
Son~Number |
1 class Father 2 { 3 public: 4 Father(){cout<<"Father() is called!"<<endl;} 5 void TestF(const int &m){ 6 i_B=m; 7 cout<<"Father::TestF() is called!"<<endl; 8 } 9 void Test(){cout<<"Base::Test() is called!"<<endl;} 10 ~Father(){cout<<"~Father() is called!"<<endl;} 11 private: 12 int i_B; 13 }; 14 15 class Son:public Father 16 { 17 public: 18 Son():i_A(5){cout<<"Son() is called!"<<endl;} 19 void Test(){cout<<"Son::Test() is called!"<<endl;} 20 ~Son(){cout<<"~Son() is called!"<<endl;} 21 private: 22 int i_A; 23 }; 24 int main(int argc,char *argv[]) 25 { 26 Father *p=new Son; 27 //Father *p=NULL; 28 p->Test(); 29 delete p; 30 p=NULL; 31 cout<<sizeof(Son)<<endl; 32 return 0; 33 }
五、单一继承(含虚函数)(虚函数表只有一个)见图:
VPTr(father) |
Father~number |
Son~number |
Child~number |
1 #include<iostream> 2 using namespace std; 3 class Father 4 { 5 public: 6 Father(){cout<<"Father() is called!"<<endl;} 7 virtual void Test(){cout<<"Base::Test() is called!"<<endl;} 8 virtual ~Father(){cout<<"~Father() is called!"<<endl;} 9 private: 10 int i_B; 11 }; 12 13 class Son:public Father 14 { 15 public: 16 Son():i_A(5){cout<<"Son() is called!"<<endl;} 17 void Test(){cout<<"Son::Test() is called!"<<endl;} 18 ~Son(){cout<<"~Son() is called!"<<endl;} 19 private: 20 int i_A; 21 }; 22 int main(int argc,char *argv[]) 23 { 24 Father *p=new Son; 25 //Father *p=NULL; 26 p->Test(); 27 delete p; 28 p=NULL; 29 cout<<sizeof(Son)<<endl; 30 return 0; 31 }
运行状况:
6、多重继承(不含虚函数)(这个也没什么好说的)
直接看代码:
1 #include<iostream> 2 using namespace std; 3 class Father 4 { 5 public: 6 Father():i_B(6){cout<<"Father() is called!"<<endl;} 7 void TestF(const int &m){ 8 i_B=m; 9 cout<<"Father::TestF() is called!"<<endl; 10 } 11 void Test(){cout<<"Father::Test() is called!"<<endl;} 12 ~Father(){cout<<"~Father() is called!"<<endl;} 13 private: 14 int i_B; 15 }; 16 class Son 17 { 18 public: 19 Son():i_A(5){cout<<"Son() is called!"<<endl;} 20 void Test(){cout<<"Son::Test() is called!"<<endl;} 21 ~Son(){cout<<"~Son() is called!"<<endl;} 22 private: 23 int i_A; 24 }; 25 26 class Child:public Father,public Son //多重继承 27 { 28 public: 29 Child():i_C(5){cout<<"Child() is called!"<<endl;} 30 void Test(){cout<<"Child::Test() is called!"<<endl;} 31 ~Child(){cout<<"~Child() is called!"<<endl;} 32 private: 33 int i_C; 34 }; 35 int main(int argc,char *argv[]) 36 { 37 Father *p=new Child; 38 //Father *p=NULL; 39 p->Test(); 40 cout<<sizeof(Son)<<endl; 41 cout<<sizeof(Child)<<endl; 42 return 0; 43 }
7、多重继承(一个含虚函数,一个不含虚函数)(相似单一继承)
VPTr(father) |
Father~number |
Son~number |
Child~number |
1 #include<iostream> 2 using namespace std; 3 4 class Father 5 { 6 public: 7 Father():i_B(6){cout<<"Father() is called!"<<endl;} 8 virtual void TestF(const int &m){ 9 i_B=m; 10 cout<<"Father::TestF() is called!"<<endl; 11 } 12 virtual void Test(){cout<<"Father::Test() is called!"<<endl;} 13 virtual ~Father(){cout<<"~Father() is called!"<<endl;} 14 private: 15 int i_B; 16 }; 17 18 class Son 19 { 20 public: 21 Son():i_A(5){cout<<"Son() is called!"<<endl;} 22 void Test(){cout<<"Son::Test() is called!"<<endl;} 23 ~Son(){cout<<"~Son() is called!"<<endl;} 24 private: 25 int i_A; 26 }; 27 28 class Child:public Father,public Son 29 { 30 public: 31 Child():i_C(5){cout<<"Child() is called!"<<endl;} 32 void Test(){cout<<"Child::Test() is called!"<<endl;} 33 ~Child(){cout<<"~Child() is called!"<<endl;} 34 private: 35 int i_C; 36 }; 37 int main(int argc,char *argv[]) 38 { 39 Father *p=new Child; 40 //Father *p=NULL; 41 p->Test(); 42 cout<<sizeof(Son)<<endl; 43 cout<<sizeof(Child)<<endl; 44 return 0; 45 }
运行状况:
8、多重继承(两个都含有虚函数)
VPTr(father) |
Father~number |
VPTr(Son) |
Son~number Child~number
20个字节 |
1 #include<iostream> 2 using namespace std; 3 4 class Father 5 { 6 public: 7 Father():i_B(6){cout<<"Father() is called!"<<endl;} 8 virtual void TestF(const int &m){ 9 i_B=m; 10 cout<<"Father::TestF() is called!"<<endl; 11 } 12 virtual void Test(){cout<<"Father::Test() is called!"<<endl;} 13 virtual ~Father(){cout<<"~Father() is called!"<<endl;} 14 private: 15 int i_B; 16 }; 17 class Son 18 { 19 public: 20 Son():i_A(5){cout<<"Son() is called!"<<endl;} 21 virtual void Test(){cout<<"Son::Test() is called!"<<endl;} 22 virtual ~Son(){cout<<"~Son() is called!"<<endl;} 23 private: 24 int i_A; 25 }; 26 27 class Child:public Father,public Son 28 { 29 public: 30 Child():i_C(7){cout<<"Child() is called!"<<endl;} 31 void Test(){cout<<"Child::Test() is called!"<<endl;} 32 ~Child(){cout<<"~Child() is called!"<<endl;} 33 private: 34 int i_C; 35 }; 36 int main(int argc,char *argv[]) 37 { 38 //Father *p=new Child; 39 Child p; 40 //Father *p=NULL; 41 p.Test(); 42 cout<<sizeof(Son)<<endl; 43 cout<<sizeof(Child)<<endl; 44 return 0; 45 }
运行状况:
9、纯虚汗继承
(只在父类中申明,并在子类中实现申明的函数才能够用)
内存分配与前面只含虚函数的状况相似
1 #include<iostream> 2 using namespace std; 3 class A 4 { 5 public: 6 A():i_A(5){cout<<"A() is called!"<<endl;} 7 public: 8 virtual void Test()= 0; //prue virtual function 9 virtual void Base() {cout<<"this is farther class"<<endl;} 10 private: 11 int i_A; 12 }; 13 14 class B:public A 15 { 16 public: 17 B():i_B(9){} 18 public: 19 void Test() { cout<<" this is SubVirtual!"<<endl;} //必须在子类中实现该函数才能够用 20 void Base() { 21 cout<<"this is subclass Base"<<endl; 22 } 23 private: 24 int i_B; 25 }; 26 27 int main(void) 28 { 29 A* p = new B; //multstate pointer 30 p->Test(); 31 p->Base(); 32 cout<<sizeof(B)<<endl; 33 return 0 ; 34 }
10、private 的虚函数
1 #include<iostream> 2 using namespace std; 3 4 class A 5 { 6 public: 7 virtual void Test() { func();} 8 private: 9 int i_A; 10 virtual void func() {cout<<"A::func () is Called!"<<endl; } 11 }; 12 class B: public A 13 { 14 private: 15 //虽然func()在A类中是private的,可是仍然能够出如今派生类中,并仍然能够与public或者protected的虚函数同样产生多态的效果。 16 virtual void func() { cout<<"B::func() is Called!"<<endl;} private: 17 int i_B; 18 }; 19 20 int main(void) 21 { 22 //A *p=new B; 23 A p; 24 //B p; 25 //p->func(); 26 p.Test(); 27 cout<<sizeof(B)<<endl; 28 return 0; 29 }
运行状况: