Think before you code, Virtual Functions in C++

Introduction

A few days back, I was doing a job, and unintentionally, I made a mistake in the code (What mistake? That I will explain in the detailed section of the article), and when I was caught by a bug and started de-bugging it, I was amazed how a little mistake can give a programmer a whole lot of pain. Yes, I made a mistake in the virtual function area. How? Let's find out........

Using the code

So, why do we need a virtual function? Everyone knows that. Let's say I have a base class and a few derived class as well; and all the derived classes shares a common function, and in the driver program, I do not want to make a big huge switch/if block. I want to iterate through all the derived types and want to execute the common member function. Like this:

Collapse | Copy Code

#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
class CommunicationDevices
{
 //Base class has some property, for this article I dont need those
 public:
   inline virtual void which(){
     cout<<"This is a common device..."<<endl;
   }
};

class MobilePhoneWithGSMSupport:public CommunicationDevices
{
  //Derived class also has some extended property, GSM related
  public:
    inline virtual void which(){
     cout<<"This is a Mobile Phone...GSM Supported"<<endl;
    }
};

class MobilePhoneWithCDMASupport:public CommunicationDevices
{
  //Derived class also has some extended property, CDMA related
  public:
    inline void which(){
      cout<<"This is a Mobile Phone....CDMA Supported"<<endl;
    }
};

class Landline:public CommunicationDevices
{
  //Derived class also has some extended property
  public:
    inline void which(){
      cout<<"This is a Landline Phone..."<<endl;
    }
};

class Iphone:public MobilePhoneWithGSMSupport
{
  //More specific IPhone Feature here
  public:
    inline void which(){
      cout<<"This is apple Iphone with AT&T connection, GSM Support only..."
          <<endl;
    }
};

void whichPhoneUserIsUsing(CommunicationDevices &devices){
  devices.which();
}

int main(){
 MobilePhoneWithGSMSupport user1;
 MobilePhoneWithCDMASupport user2;
 Landline user3;
 Iphone user4;
 whichPhoneUserIsUsing(user1);
 whichPhoneUserIsUsing(user2);
 whichPhoneUserIsUsing(user3);
 whichPhoneUserIsUsing(user4);
 return 0;
}

Here, the idea is simple. Since we are using a virtual function in the base class, the “whichPhoneUserIsUsing()” method can take a generic base class argument, and the proper method from the derived class gets accessed depending upon the actual type of the object. This is the beauty of virtual functions. Note that in the method “whichPhoneUserIsUsing()”, we used a reference to the base class as the argument: “CommunicationDevices &devices”, and from the driver (main()), we are passing the derived class' object while calling this function. This is normally called as Upcasting in C++. That is, we are going from the more specific type to the more generic type. And, this casting is type-safe always. As you expected, this code will produce the following o/p:

Collapse | Copy Code

bash-3.2$ g++ -g -o hello code1.cpp

bash-3.2$ ./hello

This is a Mobile Phone...GSM Supported
This is a Mobile Phone....CDMA Supported
This is a Landline Phone...
This is apple Iphone with AT&T connection, GSM Support only...

Now, consider the following code, only a single character (believe me, just a single character) has been changed here from the previous code:

We just modified our method whichPhoneUserIsUsing() like this:

Collapse | Copy Code

#include <iostream>
#include <vector>
using std::cout;
using std::endl;
using std::vector;
class CommunicationDevices
{
 //Base class has some property, for this article I dont need those
 public:
   inline virtual void which(){
     cout<<"This is a common device..."<<endl;
   }
};

class MobilePhoneWithGSMSupport:public CommunicationDevices
{
  //Derived class also has some extended property, GSM related
  public:
    inline virtual void which(){
     cout<<"This is a Mobile Phone...GSM Supported"<<endl;
    }
};

class MobilePhoneWithCDMASupport:public CommunicationDevices
{
  //Derived class also has some extended property, CDMA related
  public:
    inline void which(){
      cout<<"This is a Mobile Phone....CDMA Supported"<<endl;
    }
};

class Landline:public CommunicationDevices
{
  //Derived class also has some extended property
  public:
    inline void which(){
      cout<<"This is a Landline Phone..."<<endl;
    }
};

class Iphone:public MobilePhoneWithGSMSupport
{
  //More specific IPhone Feature here
  public:
    inline void which(){
      cout<<"This is apple Iphone with AT&T connection, GSM Support only..."
          <<endl;
    }
};

void whichPhoneUserIsUsing(CommunicationDevices devices){
  devices.which();
}

int main(){
 MobilePhoneWithGSMSupport user1;
 MobilePhoneWithCDMASupport user2;
 Landline user3;
 Iphone user4;
 whichPhoneUserIsUsing(user1);
 whichPhoneUserIsUsing(user2);
 whichPhoneUserIsUsing(user3);
 whichPhoneUserIsUsing(user4);
 return 0;
}

We just modified our method whichPhoneUserIsUsing() like this:

Collapse | Copy Code

void whichPhoneUserIsUsing(CommunicationDevices devices){

 devices.which();

}

and bang.................given below is the output:

Collapse | Copy Code

bash-3.2$ g++ -g -o hello code2.cpp 

bash-3.2$ ./hello

This is a common device...
This is a common device...
This is a common device...
This is a common device...

bash-3.2$ vim code2.cpp

So, what gets wrong here?

Yes, you guessed it correctly, it's a famous copy-constructor problem. When the arguments are just a “CommunicationDevices” instead of a reference to it, the function says:

Hey Mr. Programmer, I am bound to create only a temporary object for this function (whichPhoneUserIsUsing()). I am no more responsible to take a reference, so I don't care what kind of actual object you are passing through; I will create a concrete “CommunicationDevices” object, and will copy only those segments from the actual object which are meaningful to me (i.e., which are part of the base class). And, will only invoke the “which” method for this temporary object. And hence, every time you call me, I will call the base class version (i.e., CommunicationDevices version) of the which() method.

This famous property is called Object Bisection, or Object Slicing. Cutting down the desired property from one object and copying it to a concrete base class object.

时间: 2024-11-02 01:10:23

Think before you code, Virtual Functions in C++的相关文章

OGRE Code Review HOWTO

Table of Contents Introduction The Process What to Look For Style Design Guidelines OGRE Specific Guidelines Introduction The time has come for all good men to come to the aid of their favorite rendering engine. The OGRE Project needs you to help wit

Memory Layout for Multiple and Virtual Inheritance (By Edsko de Vries, January 2006)

原文地址:http://www.phpcompiler.org/articles/virtualinheritance.html In this article we explain the object layout implemented by gcc for multiple and virtual inheritance. Although in an ideal world C++ programmers should not need to know these details of

NCC Tools(never code counter tools) V1.0.1发布代码-代码统计工具_hta

界面如下图:把源代码存为(hta)文件,因为hta文件没有状态栏,所以我这里建议大家存为html文件,这样可以在状态栏下看到 NCC扫描的进度,我这里把NCC的maxloop设置为3000,所以文件统计到3000的时候,会自动终止,以防文件夹中文件太多造成运行的负担. 如果大家喜欢这样的代码,就请关注"Never Modules" 主要功能有- 1.可自己选择文件夹,或者单个文件. 2.自己选择文件后缀名进行统计 3.output information输出的数据有: 文件个数, 文件

Using SetWindowRgn

Using SetWindowRgn Home Back To Tips Page Introduction There are lots of interesting reasons for creating odd-shaped windows. This essay explains how to create a window of unusual shape. One of the first questions you should ask is "Why?" There

codeblocks 使用汇总

codeblocks 使用汇总 集成本帖提到的所有补丁,非官方,双编译器(VC9.MinGW4.4.3)绿色版,解压密码:csdn   >> http://portablecb.googlecode.com/files/LoveDEV.7z<< 由于集成VC9,所以请试用后24小时内删除! 官方公告:http://forums.codeblocks.org/index.php/topic,12156.0.html 这段时间比较忙,主要是学习Code::Blocks的代码,并且为其提

C++0x: The future of C++-- C++0x:C++的未来

C++0x: The future of C++ By Alex Allain  What is C++0x? C++0x was the working name for the new standard for C++, adding many language features that I'll cover in this series on C++11. In September 2011, C++0x was officially published as the new C++11

《深度探索C++对象模型(Inside The C++ Object Model )》学习笔记

来源:http://dsqiu.iteye.com/blog/1669614 之前一直对C++内部的原理的完全空白,然后找到<Inside The C++ Object Model>这本书看了下, 感觉收获很大,因为书写得比较早,有些知识应该要更新,但是还是值得好好研读,由于该书的内容给人比较散的感觉,所以一直想找个时间整理一下,遂成此文,虽然都是抄书上的,但是却让我有了温故而知新的觉悟,附近里有三个好资料,一并共享了!2012年9月2日凌晨 4:31 谢谢 张雨生的歌声的相伴!   <

Inside C++ object Model--构造函数

默认构造函数 构造函数是干啥的, 是在构造类对象的时候, 给程序员进行对象初始化操作的机会. 不仅如此, 同时也是给编译器进行对象初始化的机会. 当然程序员和编译器的扮演的角色是不一样的, 考虑的问题也是不一样的. 当程序员觉得这个类对象没有任何初始化的必要时, 他就不会特意去声明构造函数. 那么对于一个类, 当程序员没有声明任何构造函数的时候, 编译器有可能 会为该类声明一个default 构造函数. 之所以是'有可能', 是因为编译器也是很懒的, 如果他也觉得这个类没有任何初始化的必要时,

C++ 工程实践经验谈(转)

文章的排版不行,大家可以去http://cloud.github.com/downloads/chenshuo/documents/CppPractice.pdf 查看原文 C++ 工程实践经验谈 by 陈硕 1 C++ 工程实践经验谈 陈硕 (giantchen@gmail.com) 最后更新 2012-4-1 版权声明 本作品采用"Creative Commons 署名 -非商业性使用 -禁止演绎 3.0 Unported 许可 协议 (cc by-nc-nd)"进行许可.http