ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

【C++】多态对象拷贝

【C++】多态对象拷贝

C++11 以后,STL 容器(如:vector、list、map)的插入类接口(push_back、insert、emplace_back等)都增加了右值引用版本。比如:vector::push_back(T&& val),当传入右值时将不再进行拷贝,而是移动。

使用多态时,通过Base的指针或引用指向具体子类。在容器中,以Base类型存储,比如:vector<Base*> vec

我现在期望对vec中的每个对象,都按照其【实际的类型】进行拷贝,怎么做?拷贝构造函数又不可以设为virtual,从而进行多态实施具体行为。

  • 设一个virtual Clone(),让具体的类自己负责自己具体的类型。这是多态拷贝的标准解决方案

  • 析构函数一定要设为virtual

  • 这是原型模式(prototype pattern)的思想

  • C++支持协变返回类型

    也就是virtual Clone接口,可以返回具体类型的指针或引用,而不是只返回Base*

    • C++允许的协变返回类型仅限于原始指针和引用,不适用于智能指针

    • 智能指针和协变两者不兼容的话,在安全性(裸指针or智能指针)、类型信息缺失之间做选择,该如何取舍?

      以安全性为前提,优先使用智能指针,类型信息可通过dynaminc_cast转。

      如果类型是当前关键需求,必须使用协变,可使用【裸指针+RAII】的方式,在类析构里

    class Base
    {virtual Base* Clone()=0;	//定义Clone接口
    }class Son1:public Base
    {Son1(){}Son1(const Son1& another){}	//拷贝构造//virtual Base* Clone() const overridevirtual Son1* Clone() const override   //C++支持协变返回类型,可以返回更具体的指针,返回Son1*而不是Base*{return new Son1(*this);	//将当前对象传给本类的拷贝构造,从而返回一个本类的深拷贝对象}
    }class Son2:public Base
    {Son2(){}Son2(const Son2& another){}	//拷贝构造virtual Son2* Clone() const override{return new Son2(*this);	}
    }int main()
    {std::vector<Base*> vec;vec.push_back(new Son1);vec.push_back(new Son2);std::vector<Base*> vecCopy;for(const auto& item:vec){vecCopy.push_back(item.Clone());	//返回深拷贝对象,push_back到vecCopy中}
    }
    
  • 使用智能指针管理所有权

    防止Clone()中new出来的对象忘记delete。

    class Base
    {virtual std::unique_ptr<Base> Clone() const = 0;	
    }class Son1:public Base
    {Son1(){}Son1(const Son1& another){}	virtual std::unique_ptr<Base> Clone() const override   //不能写成std::unique_ptr<Son1>,智能指针不支持协变{//unique_ptr不允许拷贝,但编译器允许返回右值的unique_ptr。而c++中规定:return的局部变量当作右值处理//因此返回的是一个右值unique_ptr对象,传入到vec::push_back时,由于C++11起push_back(所有stl容器的插入接口)//有了参数为右值的重载:push_back(T&& value)触发push_back的移动return std::make_unique<Son1>(*this);	}
    }class Son2:public Base
    {Son2(){}Son2(const Son2& another){}	virtual std::unique_ptr<Base> Clone() const override{return std::make_unique<Son2>(*this);	}
    }int main()
    {std::vector<std::unique_ptr<Base>> vec;vec.push_back(std::make_unique<Son1>());vec.push_back(std::make_unique<Son2>());std::vector<std::unique_ptr<Base>> vecCopy;for(const auto& item:vec){vecCopy.push_back(item->Clone());	//返回右值unique_ptr,调用右值重载}
    }
    
  • 更高级实现:CRTP

返回列表