C++虚拟(Virtual)和拷贝(Copy)的若干问题

2014-11-24 11:08:53 · 作者: · 浏览: 0

C++中创立一个空类后,其实里面会自动生成一些内容,他们是:缺省构造函数,缺省拷贝构造函数 ,缺省析构函数,缺省取址运算符,和缺省赋值运算符。但在某些情况下,我们需要自定义这些函数。其中,为了实现类的多态,需要对其中的一些函数使用虚拟(Virtual)技术。总的来说,分为成员函数的虚拟,构造函数的虚拟,析构函数的虚拟,和纯虚函数等。对于拷贝相关的函数,我们需要知道在什么情况下使用拷贝构造函数,什么时候使用赋值运算符。下面来解答这些问题。


1、虚函数在成员函数中的使用。

下面代码显示了使用虚成员函数的情况。

///用作测试的基函数
class BaseClass{
public:
	int m_id;
public:
	BaseClass(int id){ m_id = id; cout<<"Base class construct. ID: "<
  
   "<
   
    "<
    
     "<
     
      Run(); cout<
      
       "<
       
        Run(); cout<
        
         "<
         
          VirtualRun(); cout<
          
           "<
           
            从上面可以看出,如果要用基类指针来调用派生类的方法,就要使用虚函数。为什么要这样做呢?举例来说,我们定义了一个“猫科动物”类,下面派生出“老虎”和“猫”两个子类,他们都有一个“叫”的方法。我们只需要一个“猫科动物”指针就可以指向“老虎”或“猫”。如果“叫”方法是虚拟的,当指针指向“老虎”时,“叫”自动调用“老虎”的叫声。对猫也一样。这就是类的“多态”(Polymorphism)。 
            


2、虚函数在析构函数中的使用

如果用基类指针指向派生类并生成了一个新的派生对象,则会发生派生类析构函数无法正确被调用的情况(调用了基类析构函数)。因此,必须使用虚析构函数,如下:

///用作测试的,使用虚析构函数,的基函数
class BaseClassVirtualDeconstructor{
public:
	int m_id;
public:
	BaseClassVirtualDeconstructor(int id){ m_id = id; cout<<"Base class construct. ID: "<
             
              "<
              
               "<
               
                "<
                
                 "<
                 
                  
3、虚函数在构造函数中的应用

把构造函数设为虚函数是没有意义的,因为虚函数是为了多态,而构造函数是对本体的。


4、纯虚函数的应用

含有至少一个纯虚函数(在函数名后加 = 0,并忽略函数体)的类叫做抽象类。抽象类不能被实例化,抽象类的派生类必须实现基类所有的纯虚函数。抽象类通常用来阻止用户对其实例化。(而仅含有虚函数的类是可以实例化的)。把构造函数设为纯虚函数也是没有意义的,也没办法通过编译。

class AbstractClass{
public:
	int m_id;
	virtual void AbstractMemberFunction() = 0;
};
class DerivedAbstractClass : AbstractClass{
public:
	DerivedAbstractClass(int id){ m_id = id; }
};
void ClassTextIV(){
	//DerivedAbstractClass derivedAbstractClass(4); ///无法通过编译
}

5、拷贝构造函数,赋值运算符拷贝,和克隆函数

既然有默认的拷贝函数,为什么还要额外定义呢?
原因1:静态变量。默认拷贝函数不处理静态变量。
原因2:浅拷贝和深拷贝。默认拷贝函数使用浅拷贝。
浅拷贝只拷贝引用对象,不单独开辟内存空间;而深拷贝要开辟内存空间。
在很多情况下,浅拷贝会带来问题。比如A被浅拷贝给了B,那么当改变A中某些值(指针指向的内存空间)的时候,B也会被连累改变。这时就需要深拷贝了。/因此,有必要定义自己的拷贝函数。


调用拷贝构造函数的情况:1、作为函数参数(传值);2、作为函数返回值;3、拷贝
比如Class c1; Class c2 = c2
拷贝构造函数是一种特殊的构造函数,它在上述情况下取代了构造函数来构造对象


虽然在拷贝构造函数的情况3中使用了赋值运算符,但是和赋值运算符拷贝不是一回事
看这种情况:Class c1; Class c2; c2 = c1;
可以看出,当被赋值的对象已经存在时,调用的是赋值运算符拷贝,而不是拷贝构造函数


克隆是一种逻辑概念,它提供了一种跟赋值运算符拷贝不同的拷贝方法
至于克隆到底要怎么实现,那么要看情况。比如,既可以浅拷贝(影子克隆),也可以深拷贝(深度克隆)

class CopyExampleClass{
public:
	int *m_id;
	CopyExampleClass(){ m_id = new int(1); }
	~CopyExampleClass(){ cout<<"析构对象: "<<*m_id<
                   
                    "<
                    
                     "<
                     
                      "<
                      
                       m_id) = 4; copyExampleClass4->PrintId();///运行结果:4 copyExampleClass.PrintId();///运行结果:1 delete copyExampleClass4; }