继承体系下的拷贝构造

1. 子类永远都不会主动调用到父类的拷贝构造函数和拷贝赋值运算符

由以下示例可以看出调用子类Car的拷贝构造函数和拷贝赋值运算符的时候并不会调用父类Vehicle的拷贝构造函数和拷贝赋值运算符(Vehicle的拷贝构造函数和拷贝赋值运算符都是delete的。)。

#include <iostream>

class Vehicle{
public:
    //Vehicle() = default;

    Vehicle(const std::string& band):band_(band){
        std::cout << "Vehicle ctor called.\n";
    }

    virtual ~Vehicle(){
        std::cout << "Vehicle dtor called.\n";
    }

    //Vehicle(const Vehicle& vehicle){
    //    band_ = vehicle.band_;
    //    std::cout << __PRETTY_FUNCTION__ << std::endl;
    //}
    Vehicle(const Vehicle& vehicle) = delete;

    //Vehicle& operator=(const Vehicle& vehicle){
    //    band_ = vehicle.band_;
    //    std::cout << __PRETTY_FUNCTION__ << std::endl;
    //    return *this;
    //}
    Vehicle& operator=(const Vehicle& vehicle) = delete;

private:
    std::string band_;
};

class Car: public Vehicle{
public:
    //Car() = default;
    Car(const std::string& band):Vehicle(band){
        std::cout << "Car ctor called.\n";
    }
    ~Car() {
        std::cout << "Car dtor called.\n";
    }

    Car(const Car& car): Vehicle("Tesla"){
        name_ = car.name_;
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    //Car(const Car& car){
    //    name_ = car.name_;
    //    std::cout << __PRETTY_FUNCTION__ << std::endl;
    //}

    Car& operator=(const Car& car){
        name_ = car.name_;
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return *this;
    }
private:
    std::string name_;
};

int main(){
    //Vehicle vec1("BYD");
    //Vehicle vec2(vec1);
    //vec2 = vec1;

    std::cout << std::string(40, '-') << std::endl;
    Car car1("BYD");
    Car car2(car1);   // 调用父类的构造函数、子类的拷贝构造,(不会调用,也不需要父类的拷贝构造)
    car2 = car1;      // 调用子类的拷贝赋值运算符,(不会调用,也不需要父类的拷贝赋值运算符)

    std::cout << std::string(40, '-') << std::endl;

    return 0;
}

输出结果:

----------------------------------------
Vehicle ctor called.
Car ctor called.
Vehicle ctor called.
Car::Car(const Car&)
Car& Car::operator=(const Car&)
----------------------------------------
Car dtor called.
Vehicle dtor called.
Car dtor called.
Vehicle dtor called.

1.1 应该在子类中主动调用父类的拷贝构造函数和拷贝赋值运算符

#include <iostream>

class Vehicle{
public:
    //Vehicle() = default;

    Vehicle(const std::string& band):band_(band){
        std::cout << "Vehicle ctor called.\n";
    }

    virtual ~Vehicle(){
        std::cout << "Vehicle dtor called.\n";
    }
    
    Vehicle(const Vehicle& vehicle){
        band_ = vehicle.band_;
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    //Vehicle(const Vehicle& vehicle) = delete;

    Vehicle& operator=(const Vehicle& vehicle){
        band_ = vehicle.band_;
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return *this;
    }
    //Vehicle& operator=(const Vehicle& vehicle) = delete;

private:
    std::string band_;
};

class Car: public Vehicle{
public:
    //Car() = default;
    Car(const std::string& band):Vehicle(band){
        std::cout << "Car ctor called.\n";
    }
    ~Car() {
        std::cout << "Car dtor called.\n";
    }

    Car(const Car& car): Vehicle(car), name_(car.name_) {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    Car& operator=(const Car& car){
        ((Vehicle*)(this))->operator=(car);
        name_ = car.name_;
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return *this;
    }
    
private:
    std::string name_;
};

int main(){
    //Vehicle vec1("BYD");
    //Vehicle vec2(vec1);
    //vec2 = vec1;

    std::cout << std::string(40, '-') << std::endl;
    Car car1("BYD");
    Car car2(car1);   // 调用子类的拷贝构造函数,子类的拷贝构造函数会调用父类的拷贝构造函数
    car2 = car1;      // 调用子类的拷贝赋值运算符,子类的拷贝赋值运算符内部会调用父类的拷贝赋值运算符

    std::cout << std::string(40, '-') << std::endl;
}

// 输出结果:

----------------------------------------
Vehicle ctor called.
Car ctor called.
Vehicle::Vehicle(const Vehicle&)
Car::Car(const Car&)
Vehicle& Vehicle::operator=(const Vehicle&)
Car& Car::operator=(const Car&)
----------------------------------------
Car dtor called.
Vehicle dtor called.
Car dtor called.
Vehicle dtor called.

1.2 使用copy-and-swap idiom

【吴咏炜老师的解答】这样的赋值能提供强异常安全保证,也不需要自赋值检查。但性能上不及传统的实现方式。
在以面向对象方式实现的对象里,通常不会支持赋值操作。连拷贝构造都不会直接提供——因为你不知道指针/引用指向的是不是派生类对象。
参见 C++ 核心指南里的 C.67 和 C.130 条款。
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines

#include <iostream>
#include <string>

class Vehicle{
public:
    //Vehicle() = default;
    Vehicle(const std::string& brand):brand_(brand){
        std::cout << "Vehicle ctor called.\n";
    }

    virtual ~Vehicle(){
        std::cout << "Vehicle dtor called.\n";
    }

    Vehicle(const Vehicle& rhs): brand_(rhs.brand_){
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    //Vehicle(const Vehicle& vehicle) = delete;

    Vehicle& operator=(const Vehicle& rhs){
        Vehicle(rhs).swap(*this);
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return *this;
    }
    //Vehicle& operator=(const Vehicle& vehicle) = delete;

    void swap(Vehicle& rhs) noexcept {
        brand_.swap(rhs.brand_);
    }

private:
    std::string brand_;
};

class Car: public Vehicle{
public:
    //Car() = default;
    Car(const std::string& brand):Vehicle(brand){
        std::cout << "Car ctor called.\n";
    }
    ~Car() {
        std::cout << "Car dtor called.\n";
    }

    Car(const Car& rhs): Vehicle(rhs), name_(rhs.name_) {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    Car& operator=(const Car& rhs){
        Car(rhs).swap(*this);

        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return *this;
    }

    void swap(Car& rhs) noexcept {
        static_cast<Vehicle&>(*this).swap(rhs);

        name_.swap(rhs.name_);
    }

private:
    std::string name_;
};

int main(){
    //Vehicle vec1("BYD");
    //Vehicle vec2(vec1);
    //vec2 = vec1;

    std::cout << std::string(40, '-') << std::endl;
    Car car1("BYD");
    Car car2(car1);
    car2 = car1;

    std::cout << std::string(40, '-') << std::endl;
}

输出结果:

----------------------------------------
Vehicle ctor called.
Car ctor called.
Vehicle::Vehicle(const Vehicle&)
Car::Car(const Car&)
Vehicle::Vehicle(const Vehicle&)
Car::Car(const Car&)
Car dtor called.
Vehicle dtor called.
Car& Car::operator=(const Car&)
----------------------------------------
Car dtor called.
Vehicle dtor called.
Car dtor called.
Vehicle dtor called.

2. 继承体系下一般通过clone来实现对象的拷贝

#include <iostream>
#include <memory>

class Vehicle{
public:
    Vehicle(const std::string& band):band_(band){
        std::cout << "Vehicle ctor called.\n";
    }

    virtual ~Vehicle(){
        std::cout << "Vehicle dtor called.\n";
    }

    //Vehicle(const Vehicle& vehicle){
    //    band_ = vehicle.band_;
    //    std::cout << __PRETTY_FUNCTION__ << std::endl;
    //}
    Vehicle(const Vehicle& vehicle) = delete;

    //Vehicle& operator=(const Vehicle& vehicle){
    //    band_ = vehicle.band_;
    //    std::cout << __PRETTY_FUNCTION__ << std::endl;
    //    return *this;
    //}
    Vehicle& operator=(const Vehicle& vehicle) = delete;

    virtual std::shared_ptr<Vehicle> clone() const = 0;

private:
    std::string band_;
};

class Car: public Vehicle{
public:
    //Car() = default;
    Car(const std::string& band):Vehicle(band){
        std::cout << "Car ctor called.\n";
    }
    ~Car() {
        std::cout << "Car dtor called.\n";
    }

    Car(const Car& car): Vehicle("Tesla"){
        name_ = car.name_;
        std::cout << __PRETTY_FUNCTION__ << ", Car copy-ctor called." << std::endl;
    }

    //Car(const Car& car){
    //    name_ = car.name_;
    //    std::cout << __PRETTY_FUNCTION__ << std::endl;
    //}

    Car& operator=(const Car& car){
        name_ = car.name_;
        std::cout << __PRETTY_FUNCTION__ << ", Car copy-assignment called." << std::endl;
        return *this;
    }

    virtual std::shared_ptr<Vehicle> clone() const override{
        return std::shared_ptr<Vehicle>(new Car(*this));
    }

private:
    std::string name_;
};

int main(){
    std::cout << std::string(40, '-') << std::endl;

    Car car1("BYD");
    std::shared_ptr<Vehicle> vehicle = car1.clone();

    std::cout << std::string(40, '-') << std::endl;

    return 0;
}

输出:

----------------------------------------
Vehicle ctor called.
Car ctor called.
Vehicle ctor called.
Car::Car(const Car&), Car copy-ctor called.
----------------------------------------
Car dtor called.
Vehicle dtor called.
Car dtor called.
Vehicle dtor called.

如上述示例,利用clone对象的方式可以不用先构造出对象(通常调用拷贝构造函数)再将对象的地址赋给父类指针,而是直接使用已有对象的clone方法返回新创建对象的地址赋给父类指针。

3. 使用CRTP来实现继承体系下对象的拷贝

#include <iostream>
#include <memory>

template<typename ChildType>
class Vehicle{
public:
    Vehicle(const std::string& band):band_(band){
        std::cout << "Vehicle ctor called.\n";
    }

    virtual ~Vehicle(){
        std::cout << "Vehicle dtor called.\n";
    }

    //Vehicle(const Vehicle& vehicle){
    //    band_ = vehicle.band_;
    //    std::cout << __PRETTY_FUNCTION__ << std::endl;
    //}
    Vehicle(const Vehicle& vehicle) = delete;

    //Vehicle& operator=(const Vehicle& vehicle){
    //    band_ = vehicle.band_;
    //    std::cout << __PRETTY_FUNCTION__ << std::endl;
    //    return *this;
    //}
    Vehicle& operator=(const Vehicle& vehicle) = delete;

    virtual std::shared_ptr<Vehicle> clone() const {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return std::shared_ptr<Vehicle>(new ChildType(static_cast<const ChildType &>(*this)));
    }

private:
    std::string band_;
};

class Car: public Vehicle<Car>{
public:
    //Car() = default;
    Car(const std::string& band):Vehicle(band){
        std::cout << "Car ctor called.\n";
    }
    ~Car() {
        std::cout << "Car dtor called.\n";
    }

    Car(const Car& car): Vehicle("Tesla"){
        name_ = car.name_;
        std::cout << __PRETTY_FUNCTION__ << ", Car copy-ctor called." << std::endl;
    }

    //Car(const Car& car){
    //    name_ = car.name_;
    //    std::cout << __PRETTY_FUNCTION__ << std::endl;
    //}

    Car& operator=(const Car& car){
        name_ = car.name_;
        std::cout << __PRETTY_FUNCTION__ << ", Car copy-assignment called." << std::endl;
        return *this;
    }

private:
    std::string name_;
};

int main(){
    std::cout << std::string(40, '-') << std::endl;

    Car car1("BYD");
    std::shared_ptr<Vehicle<Car>> vehicle = car1.clone();

    std::cout << std::string(40, '-') << std::endl;

    return 0;
}

输出结果:

----------------------------------------
Vehicle ctor called.
Car ctor called.
std::shared_ptr<Vehicle<ChildType> > Vehicle<ChildType>::clone() const [with ChildType = Car]
Vehicle ctor called.
Car::Car(const Car&), Car copy-ctor called.
----------------------------------------
Car dtor called.
Vehicle dtor called.
Car dtor called.
Vehicle dtor called.

4.什么是CRTP?

CRTP(Curiously Recurring Template Pattern) 奇异递归模板模式。
使用CRTP的目的是父类在编译时就可以知道子类的信息,将多态调用转为编译期绑定,节省了虚函数调用的开销。不使用CRTP时是虚函数调用,使用CRTP时在编译期利用子类作为模板参数,提早绑定子类,而不是运行时绑定。参考:Design Patterns With C++(八)CRTP(上)

4.1 使用CRTP实现编译时多态

虚函数版本:

#include <iostream>
#include <memory>

template<typename ChildType>
class Vehicle{
public:
    Vehicle(const std::string& band):band_(band){
        std::cout << "Vehicle ctor called.\n";
    }

    virtual ~Vehicle(){
        std::cout << "Vehicle dtor called.\n";
    }

    Vehicle(const Vehicle& vehicle) = delete;

    Vehicle& operator=(const Vehicle& vehicle) = delete;

    virtual std::shared_ptr<Vehicle> clone() const {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return std::shared_ptr<Vehicle>(new ChildType(static_cast<const ChildType &>(*this)));
    }

    /* 此处的drive()是通过虚函数实现的,也可以不用虚函数 */
    virtual void drive() {
        ((ChildType*)this)->drive();
    }

private:
    std::string band_;
};

class Car: public Vehicle<Car>{
public:
    Car(const std::string& band):Vehicle(band){
        std::cout << "Car ctor called.\n";
    }
    ~Car() {
        std::cout << "Car dtor called." << std::endl;
    }

    Car(const Car& car): Vehicle("Tesla"){
        std::cout << __PRETTY_FUNCTION__ << ", Car copy-ctor called." << std::endl;
    }

    Car& operator=(const Car& car){
        std::cout << __PRETTY_FUNCTION__ << ", Car copy-assignment called." << std::endl;
        return *this;
    }

    /* 此处的drive()是通过虚函数实现的,也可以不用虚函数 */
    virtual void drive() override {
        std::cout << "Driving a car.\n";
    }
};

class dummy: public Vehicle<dummy>{
public:
    dummy() = default;
    ~dummy() = default;
    dummy(const dummy&) = delete;
    dummy& operator=(const dummy&) = delete;
};

int main(){
    std::cout << std::string(40, '-') << std::endl;

    std::shared_ptr<Vehicle<Car>> vehicle(new Car("BYD"));
    vehicle->drive();

    std::cout << std::string(40, '-') << std::endl;

    return 0;
}

输出:

----------------------------------------
Vehicle ctor called.
Car ctor called.
Driving a car.
----------------------------------------
Car dtor called.
Vehicle dtor called.

非虚函数版本:

#include <iostream>
#include <memory>

template<typename ChildType>
class Vehicle{
public:
    Vehicle(const std::string& band):band_(band){
        std::cout << "Vehicle ctor called.\n";
    }

    virtual ~Vehicle(){
        std::cout << "Vehicle dtor called.\n";
    }

    Vehicle(const Vehicle& vehicle) = delete;

    Vehicle& operator=(const Vehicle& vehicle) = delete;

    virtual std::shared_ptr<Vehicle> clone() const {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return std::shared_ptr<Vehicle>(new ChildType(static_cast<const ChildType &>(*this)));
    }

    /* 非虚函数,但结合CRTP可以实现编译时多态 */
    void drive() {
        ((ChildType*)this)->driveImp();
    }

private:
    std::string band_;
};

class Car: public Vehicle<Car>{
public:
    Car(const std::string& band):Vehicle(band){
        std::cout << "Car ctor called.\n";
    }
    ~Car() {
        std::cout << "Car dtor called." << std::endl;
    }

    Car(const Car& car): Vehicle("Tesla"){
        std::cout << __PRETTY_FUNCTION__ << ", Car copy-ctor called." << std::endl;
    }

    Car& operator=(const Car& car){
        std::cout << __PRETTY_FUNCTION__ << ", Car copy-assignment called." << std::endl;
        return *this;
    }

    /* 非虚函数,但结合CRTP可以实现编译时多态 */
    void driveImp() {
        std::cout << "Driving a car.\n";
    }
};

class dummy: public Vehicle<dummy>{
public:
    dummy() = default;
    ~dummy() = default;
    dummy(const dummy&) = delete;
    dummy& operator=(const dummy&) = delete;
};

int main(){
    std::cout << std::string(40, '-') << std::endl;

    std::shared_ptr<Vehicle<Car>> vehicle(new Car("BYD"));
    vehicle->drive();

    std::cout << std::string(40, '-') << std::endl;

    return 0;
}

输出:

----------------------------------------
Vehicle ctor called.
Car ctor called.
Driving a car.
----------------------------------------
Car dtor called.
Vehicle dtor called.

4.2 使用虚函数实现运行时多态

#include <iostream>
#include <memory>

template<typename ChildType>
class Vehicle{
public:
    Vehicle(const std::string& band):band_(band){
        std::cout << "Vehicle ctor called.\n";
    }

    virtual ~Vehicle(){
        std::cout << "Vehicle dtor called.\n";
    }

    Vehicle(const Vehicle& vehicle) = delete;

    Vehicle& operator=(const Vehicle& vehicle) = delete;

    virtual std::shared_ptr<Vehicle> clone() const {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return std::shared_ptr<Vehicle>(new ChildType(static_cast<const ChildType &>(*this)));
    }

    /* 虚函数,实现运行时多态 */
    virtual void drive() = 0;

private:
    std::string band_;
};

class Car: public Vehicle<Car>{
public:
    Car(const std::string& band):Vehicle(band){
        std::cout << "Car ctor called.\n";
    }
    ~Car() {
        std::cout << "Car dtor called." << std::endl;
    }

    Car(const Car& car): Vehicle("Tesla"){
        std::cout << __PRETTY_FUNCTION__ << ", Car copy-ctor called." << std::endl;
    }

    Car& operator=(const Car& car){
        std::cout << __PRETTY_FUNCTION__ << ", Car copy-assignment called." << std::endl;
        return *this;
    }

    /* 虚函数,实现运行时多态 */
    virtual void drive() override {
        std::cout << "Driving a car.\n";
    }
};

class dummy: public Vehicle<dummy>{
public:
    dummy() = default;
    ~dummy() = default;
    dummy(const dummy&) = delete;
    dummy& operator=(const dummy&) = delete;
};

int main(){
    std::cout << std::string(40, '-') << std::endl;

    std::shared_ptr<Vehicle<Car>> vehicle(new Car("BYD"));
    vehicle->drive();

    std::cout << std::string(40, '-') << std::endl;

    return 0;
}

输出:

----------------------------------------
Vehicle ctor called.
Car ctor called.
Driving a car.
----------------------------------------
Car dtor called.
Vehicle dtor called.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值