c++ 基类指针作为私有成员

w41d8nur  于 2023-02-14  发布在  其他
关注(0)|答案(1)|浏览(153)

我正在一个类有一个私有成员的系统上工作,这个私有成员是一个基类指针。这个指针可以指向一个继承的类对象。我希望参数化和复制构造函数能够获取基类类型的指针,并在不删除继承类型的情况下深度复制它。下面是我为演示这个问题而编写的一些代码;我想让c2和c3调用B.print而不是A.print,但不知道如何调用。

#include <iostream>

class A
{
protected:
    double a;
    double b;

public:
    A()
    {
        a = 0.0;
        b = 0.0;
    }

    A(double a, double b)
    {
        this->a = a;
        this->b = b;
    }

    A(const A& copy)
    {
        a = copy.a;
        b = copy.b;
    }

    virtual void print()
    {
        std::cout << "print A" << std::endl;
        std::cout << "a: " << a << ", b: " << b << std::endl;
    }
};

class B : public A
{
private:
    double c;

public:
    B()
    {
        c = 0.0;
    }

    B(double a, double b, double c) : A(a, b)
    {
        this->c = c;
    }

    B(const B& copy) : A(copy)
    {
        c = copy.c;
    }

    virtual void print()
    {
        std::cout << "print B" << std::endl;
        std::cout << "a: " << a << ", b: " << b << ", c: " << c << std::endl;
    }
};

class C
{
private:
    A* a;

public:
    C()
    {
        a = nullptr;
    }

    C(A* a)
    {
        this->a = new A(*a);
    }

    C(const C& copy)
    {
        a = nullptr;
        if (copy.a != nullptr)
            a = new A(*copy.a);
    }

    ~C()
    {
        delete a;
        a = nullptr;
    }

    void print()
    {
        if (a != nullptr)
            a->print();
    }
};

int main()
{
    A* a = new A(3.5, 4.5);
    A* b = new B(3.5, 4.5, 5.5);
    C c1(a), c2(b), c3(c2);
    delete a, b;

    c1.print();
    c2.print();
    c3.print();
}

我尝试过包含可用类型的枚举并单独存储该值,但该程序是不可扩展的,因为它需要为每个添加的继承类型更改开关和枚举。

avkwfej4

avkwfej41#

您的实际问题在于以下几行:

C(A* a) {
        this->a = new A(*a);
    }

    C(const C& copy) {
        a = nullptr;
        if (copy.a != nullptr)
            a = new A(*copy.a);
    }

它 * 显式地 * 创建一个动态类型为A的对象,忽略动态类型acopy.a
复制某个对象的真实的动态类型的通常模式是

struct Base
{
    // copy ctor etc. as usual
    virtual Base* clone() const { return new Base(*this); }
};

struct Derived: Base
{
    virtual Base* clone() const { return new Derived(*this); }
};

其他,杂项注解:

  • 你不需要在一个默认的构造函数上写6行代码,这个构造函数什么也不做。
class A
{
protected:
    double a{};
    double b{};

让编译器默认它。你也可以默认你的大多数复制构造函数。

  • 如果你要控制指针的生存期(就像C那样),就使用std::unique_ptr,它就在那里,它是免费的,它的语义更加明确。
  • 当你通过一个基类指针拥有一个多态对象(即控制其生存期)时,这个基类需要有一个虚析构函数(除非你用一个类型擦除的删除器做一些奇怪的事情,这超出了这里的范围)。
  • 如果你已经决定使用this->a作为数据成员,那么要始终如一地使用它;如果你只是为了消除歧义而使用它,那么只要改变参数/局部变量名,这样它就不会在第一时间冲突。

有一些成员命名约定,比如m_aa_,这使得很容易看到哪些变量是数据成员,消除了歧义,并且比随机散布this->更少的键入/视觉噪音。
仅进行上述变更的工作代码:

#include <iostream>
#include <memory>

class A
{
protected:
    double a_{};
    double b_{};

public:
    A() = default;
    A(double a, double b) : a_(a), b_(b) {}
    A(const A&) = default;
    virtual ~A() = default;
    virtual A* clone() const { return new A(*this); }

    virtual void print()
    {
        std::cout << "print A" << '\n'
                  << "a: " << a_ << ", b: " << b_ << '\n';
    }
};

class B : public A
{
    double c_{};

public:
    B() = default;
    B(double a, double b, double c) : A(a, b), c_(c) {}
    B(const B&) = default;

    virtual B* clone() const override { return new B(*this); }

    virtual void print() override
    {
        std::cout << "print B" << '\n'
                  << "a: " << a_ << ", b: " << b_ << ", c: " << c_ << '\n';
    }
};

// owns and deep-copies an A.
// class invariant: ptr_ is non-null
//
class C
{
private:
    std::unique_ptr<A> ptr_;

public:
    explicit C(A* a) : ptr_(a->clone()) {}
    C(const C& copy) : ptr_(copy.ptr_->clone()) {}

    void print()
    {
        ptr_->print();
    }
};

int main()
{
    A a{3.5, 4.5};
    B b{3.5, 4.5, 5.5};
    C c1(&a), c2(&b), c3(c2);

    c1.print();
    c2.print();
    c3.print();
}

注意:即使您 * 确实 * 希望C进行深度复制,顶层动态分配也是不必要的,所以我只是将其完全删除,而不是将其更改为也使用unique_ptr或修复不正确的delete语句。

相关问题