Think before you code, Virtual Functions in C++
2009-09-15 10:42
471 查看
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:
![](http://www.codeproject.com/images/minus.gif)
Collapse
![](http://www.codeproject.com/images/copy_16.png)
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:
![](http://www.codeproject.com/images/minus.gif)
Collapse
![](http://www.codeproject.com/images/copy_16.png)
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:
![](http://www.codeproject.com/images/minus.gif)
Collapse
![](http://www.codeproject.com/images/copy_16.png)
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:
![](http://www.codeproject.com/images/minus.gif)
Collapse
![](http://www.codeproject.com/images/copy_16.png)
Copy Code
void whichPhoneUserIsUsing(CommunicationDevices devices){
devices.which();
}
and bang.................given below is the output:
![](http://www.codeproject.com/images/minus.gif)
Collapse
![](http://www.codeproject.com/images/copy_16.png)
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.
References:
C++ Programming Language: Bjarne Stroustrup
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
About the Author
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:
![](http://www.codeproject.com/images/minus.gif)
Collapse
![](http://www.codeproject.com/images/copy_16.png)
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:
![](http://www.codeproject.com/images/minus.gif)
Collapse
![](http://www.codeproject.com/images/copy_16.png)
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:
![](http://www.codeproject.com/images/minus.gif)
Collapse
![](http://www.codeproject.com/images/copy_16.png)
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:
![](http://www.codeproject.com/images/minus.gif)
Collapse
![](http://www.codeproject.com/images/copy_16.png)
Copy Code
void whichPhoneUserIsUsing(CommunicationDevices devices){
devices.which();
}
and bang.................given below is the output:
![](http://www.codeproject.com/images/minus.gif)
Collapse
![](http://www.codeproject.com/images/copy_16.png)
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.
References:
C++ Programming Language: Bjarne Stroustrup
License
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
About the Author
programmersmind![]() Member | int main(){ while(!isSleeping()) { write_code(); } return 0; }
|
相关文章推荐
- Think before you code, Virtual Functions in C++
- You receive a C4226 or a C4236 error message when you compile code to contain the keyword in 32-bit Visual C++(ZZ)
- Can static functions be virtual in C++?
- How do virtual functions work in C++?
- Pure Virtual Functions and Abstract Classes in C++
- self-awareness is key in changing the way you think
- Deleting a Mounted Folder The code example in this topic shows you how to delete a mounted folder by
- usage of fscanf and other read functions in C/C++
- You cannot access the client's file system using the FileSystemObject in your ASP code
- .net垃圾回收学习【NET Best Practice No: 1:- Detecting High Memory consuming functions in .NET code】【翻译&&学习】
- Writing shellcode in C++
- Prototype in C++: Before and after
- string to number code in C++ template
- Learning to Code after 40: If You Think It’s Too Late, Read This(1)
- [C++] OOP - Virtual Functions and Abstract Base Classes
- rror Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode, toggle the option in Preferences -> SQL Editor and reconnec
- (C/C++ )Interview in English - Virtual
- Inline Functions in C++
- Why would you learn C++ in 2016?(2016年,为什么我们要学习C++)
- What is the best(funniest) comment in source code you have ever encountered?