所谓虚函数是指:
在类中被声明为virtual的成员,基类希望这种成员在派生类中重定义。除了构造函数外,任意非static成员都可以为虚成员。保留字 virtual 只在类内部的成员函数声明中出现,不能用在类定义体外部出现在函数定义上。
protected标号:
protected为受保护的访问标号,protected成员可以被该类的成员、友元和派生类成员(非友元)访问,而不可以被该类型的普通用户访问。
而private成员,只能被基类的成员和友元访问,派生类不能访问。
派生类只能通过派生类对象访问其基类的protected成员,派生类对其基类类型对象的protected成员没有特殊访问权限。
派生类中虚函数的声明必须与基类中的定义方式完全匹配,但有一个例外:返回对基类型的引用(或指针)的虚函数。派生类中的虚函数可以返回基类函数所返回类型的派生类的引用(或指针),或返回基类类型的引用(或指针);
一旦函数在基类中声明为虚函数,它就一直为虚函数,派生类无法改变该函数为虚函数的这一事实。派生类重定义虚函数时,可以使用 virtual 保留字,但是不是必须这样做。
已定义的类才可以做基类。如果已经声明了一个类,但是没有定义它,则不能用作基类。
如果需声明(但不实现)一个派生类,则声明包含类名但不包含派生列表。例如,下边的前向声明会导致编译时错误:
//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,则派生类列表中使用的访问标号决定该成员在派生类中的访问级别:
1、如果是公有继承,基类成员保持自己的访问级别:基类的public成员为派生类的public成员,基类的protected成员为派生类的protected成员;
2、如果是受保护继承,基类的public和protected成员在派生类中为protected成员;
3、如果是私有继承,基类的所有成员在派生类中为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 };
关于虚函数的应用(10个例子)
虚函数是C++中非常重要的一个概念,它最大的好处是能够触发动态绑定。C++中的函数默认不使用动态绑定,要触发动态绑定,必须满足 两个条件:
第一,只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不进行动态绑定;
第二,必须通过基类类型的指针或引用进行函数的调用。具体理解指针或引用在使用继承层次中某一类型的对象时会发生什么,本文不展开讨论,
这两天主要研习了虚函数的具体应用这一块,而它的应用又非常广泛,学MFC的应该能够感受到它的强大,要说是总结也不一定能够总结全,本人目前也处在studying中,所以用10个具体的例子来说明。例子是从难 到易,看到的朋友如果懂前面的可以不用看后面的。每一个例子就是一个类,通过类在内存中的布局来形象地分析虚函数究竟是如何运作的。图表示可能抽象一点,一般带有V开头的表示一个虚函数表,如果是学过编译原理这门课就很容易看懂,没学过的只要懂虚函数的一些机制,耐着性子也是没问题的。每个图示都配有相应的代码。可以对照着代码来看。
1、 虚函数继承的复杂例子
2、 菱形继承无虚拟继承的情况
3、 虚拟继承的简单情况
4、 单一普通继承(无虚函数)
5、 单一继承(含虚函数)(虚函数表只有一个)
6、 多重继承(不含虚函数)
7、 多重继承(一个含虚函数,一个不含虚函数)
8、 多重继承(两个都含有虚函数)
9、 纯虚汗继承
10、 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 }
运行情况:
2、 菱形继承无虚拟继承的情况
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 }
运行情况:
3、 虚拟继承的简单情况 见下图:
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 }
5、单一继承(含虚函数)(虚函数表只有一个)见图:
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 }
运行情况: