符合标准容器的抽象类型

时间:2016-07-29 08:20:17

标签: c++ oop c++11

我一直在尝试解决涉及抽象类型及其派生类以及std容器的设计问题。我有一个有效的解决方案,但我想看看它是否可以改进。我为“......”道歉。但它是为了使设计问题更加清晰。

// the base class
class B { 
private:
    ...
public:
    // the virtual function which makes B an abstract type
    virtual void func(...) = 0; 
    ...
};

// the 1st derived class
class D1 : public B { 
    ...
    void func(...) {...}
};

// the 2nd derived class
class D2 : public B {
    ...
    void func(...) {...}
};

根据问题,D1D2必须指向包含 B w /的某些实例o了解他们的具体类型。目前,我正在使用指向方法,这意味着D1D2包含一些指向B的指针。

此外,在递归合成中B的更高级实例中,B的那些指向实例实际上是共享。为了说清楚,我将扩展D1的定义。

class D1 : public B { 
private:
    B *d1_b1;
    B *d1_b2;
    ...
public:
    D1 (B *b1, B *b2) {...}
    B* func(...) {...}
    ...
};

现在,虽然d1_b1d1_b2的某些更高级别的实例共享D1D2,但我认为没有更高级别的对象应保持较低级别对象的所有权或生命周期。相反,我使用带有unique_ptr s的std容器,例如:

std::vector<std::unique_ptr<B>> unique_b_objs;

unique_b_objs在首次调用递归构造的范围内声明,并通过引用传递并递归使用。

现在,每次创建D1D2的新对象时,相应的unique_ptr都会被推回unique_b_objs。基本上, B对象的原始指针用于递归合成,但 B的对象的生命周期和所有权由std容器管理

我希望到目前为止我已经说清楚了。它按预期工作。但我正在努力摆脱原始指针。我可以想到两种方法,但它们现在都不起作用。

  • 使用“包含”方法。基本上,B *B中的所有D1变为D2。但B是一种抽象类型,这意味着编译器甚至不会编译类似D1 (B b1, B b2) {...}
  • 的内容。
  • 在容器中使用引用。可以通过reference_wrapper完成。但是,由于对象是在递归中本地创建的,因此引用在范围之外变得无效。这意味着我们无法在递归调用之间传输引用。

一种工作方法是使用shared_ptr。但是没有必要在每个递归步骤中共享所有权。它还会导致性能开销。

此时,感谢您阅读问题。我希望我已经说清楚了。如果你能想到一些比unique_ptr s +原始指针容器更好的方法,请告诉我。

1 个答案:

答案 0 :(得分:0)

使用非拥有原始指针(例如,请参阅this answer)完全没问题 如果您的目标是在构造函数和派生类的成员中使用引用而不是指针,则可以在不对设计进行重大更改的情况下执行此操作。
例如,

#include <iostream>
#include <vector>
#include <memory>
class B {
private:
public:
    virtual void func() = 0;
};

std::vector<std::unique_ptr<B>> arena;

class D1 : public B {
    B &b1;
    B &b2;
public:
    D1(B &b1, B &b2) : b1(b1), b2(b2) { }
    void func() {
        std::cout << "D1\n";
        b1.func();
        b2.func();
    }
};

class D2 : public B {
    B &b;
public:
    D2(B &b) : b(b) { }
    void func() {
        std::cout << "D2\n";
        b.func();
    }
};

class D3 : public B {
public:
    D3() { }
    void func() {
        std::cout << "D3\n";
    }
};

int main() {
    auto node = std::make_unique<D3>(); // make new object
    auto& nodeRef = *node; // get reference to the object
    arena.push_back(std::move(node)); // transfer ownership to the vector,
                                      // nodeRef is not invalidated

    auto node1 = std::make_unique<D2>(nodeRef); // use reference
    auto& node1Ref = *node1;
    arena.push_back(std::move(node1));

    auto node2 = std::make_unique<D3>();
    auto& node2Ref = *node2;
    arena.push_back(std::move(node2));

    auto node3 = std::make_unique<D1>(node1Ref, node2Ref);
    arena.push_back(std::move(node2));
    node3->func();
}