C ++:通过'扩展来创建派生实例?'基础实例/实现向下转换?

时间:2016-05-08 06:22:15

标签: c++

我有一个C ++类,它本质上是一个容器:

socket-accept

除了class SimpleContainer { public: // Various accessor methods / iterators private: // The actual content } 之外 - 我想创建一个SimpleContainer,它通过对内容强制执行规则来语义扩展StrictContainer,但使用与现有{{1}相同的内容}实例。

SimpleContainer

现在 - 我的问题是SimpleContainerclass StrictContainer : public SimpleContainer { StrictContainer( const SimpleContainer& simple ) { // Check content in simple, and raise an exception // if it is not valid. } } 之间应该有什么样的关系:

是-A : 看起来自然;这就是我用继承表示的内容,但后来我将基于扩展SimpleContainer实例帖子构建创建一个StrictContainer实例 - 这可能吗?

具有-A 或者,我可以将其实现为 has-a 关系,其中StrictContainer具有SimpleContainer成员,但是我需要在{{}}中再次实现所有访问器和迭代器{1}}转发StrictContainer实施。

容器相当大;在创建SimpleContainer时,我真的不会复制内容。我想我想要的是一种实现我自己的低位运算符的方法:

StrictContainer

SimpleContainer会调用StrictContainer上的方法来验证SimpleContainer simple; // Fill simpleContainer { StrictContainer* strict = downcast<StrictContainer>( simple ) ... } 输入参数的内容吗?

4 个答案:

答案 0 :(得分:2)

is-a 将是一场灾难。假设规则是SimpleContainer包含整数,StrictContainer只包含奇数整数。

StrictContainer strict;
SimpleContainer simple;
strict.insert(1);  // OK value is odd
simple.insert(2);  // OK simple doesn't check oddness.
SimpleContainer& sc = strict;  // Reference to base.
sc.SimpleContainer::insert(2);  // Uh-oh.  That will use the simple container version
                                // of insert (even in the presence of virtual functions)
                                // and will insert an invalid even number into `simple`.

您需要 has-a

答案 1 :(得分:2)

作为第三种选择,我建议采用基于策略的方法,如下例所示:

#include<vector>
#include<cassert>

template<typename T>
struct NoCheck {
    static bool isOk(T) {
        return true;
    }
};

template<typename T>
struct TheAnswer {
    static bool isOk(T t) {
        return false;
    }
};

template<>
struct TheAnswer<int> {
    static bool isOk(int t) {
        return t == 42;
    }
};

template<typename T, template<typename> class C = NoCheck>
struct SimpleContainer {
    bool add(T t) {
        if(C<T>::isOk(t)) {
            vec.push_back(t);
            return true;
        }

        return false;
    }

private:
    std::vector<T> vec{};
};

int main() {
    SimpleContainer<int> c1;
    assert(c1.add(42));
    assert(c1.add(0));
    SimpleContainer<int, TheAnswer> c2;
    assert(c2.add(42));
    assert(not c2.add(0));
}

如果要求使用通用接口(问题不明确),您可以使用工厂方法和类型擦除的内部类仍然基于策略。

答案 2 :(得分:0)

所以,重申你的要求:

  • StrictContainerSimpleContainer具有相同的界面, 添加。 (这意味着与 的关系 接口。)
  • 不应要求实例化新的StrictContainer 复制SimpleContainer内容。 (这意味着使用句柄进行分解。)

假设原始SimpleContainer的生命周期跨越StrictContainer,这听起来像decorator pattern。所以,如果你愿意做一些分解 - 如何定义这样的界面呢?

class MyInterface {
  // No contained state needed at all.
public:
  virtual ~MyInterface() {}
  virtual bool getThing1() const = 0;
  virtual int  getThing2() const = 0;
  // and so on...
};

class MyDecorator : public MyInterface {
    MyInterface &m_impl; // Could use a smart pointer, if available.
public:
    MyDecorator( MyInterface &impl ): m_impl(impl) {}
    bool getThing1() const override { return impl.getThing1(); }
    int  getThing2() const override { return impl.getThing2(); }
    // and so on...
};

然后你的具体实现:

class SimpleContainer : public MyInterface {
    bool m_thing1;
    int  m_thing2;
        ... // All the real state goes here.
public:
        ... // All the interface methods go here.
};

然后,StrictContainer是一个轻量级容器,它保留原始SimpleContainer的句柄,而不是完整副本。

class StrictContainer : public MyDecorator {
    // Additional state, if needed.
public:
    StrictContainer( SimpleContainer &base ): MyDecorator(base) {}
    // Additional methods (like your additional validation method.
};

由于您已经需要调用StrictContainer的构造函数并且它已经完成了您需要的所有操作,因此不需要特殊的&#34;向下转换运算符&#34;

SimpleContainer simple( ... );   // Construct as normal.
StrictContainer strict = simple; // Assuming no extra arguments needed.

非常简单。

是的,您需要在装饰器类的界面 一次 中为每个方法编写委派操作。但您可以定义StrictContainer类的多个变体,而无需再次实现它们(除非 想要 覆盖其中一些)。

答案 3 :(得分:0)

我更喜欢使用适配器和策略(策略)模式的组合来实现这样的东西。您可以将StrictContainer(它是适配器)与完全保存数据的基础容器分离。非常类似于std :: queue如何实现为另一个容器(如vector)的适配器。然后,您可以使用您想要的任何约束来参数化StrictContainer适配器。每当向容器添加元素时,都会检查是否满足约束。如果是,则将元素添加到底层容器中,否则您可以执行任何您喜欢的操作(例如,保持容器不变或抛出异常)。

template<typename T, typename Container, typename Constraint>
class StrictContainer
{
public:
    StrictContainer(const Container& container = {}, Constraint constraint = {})
       : container_(container), constraint_(constraint)
    {
        validateAll();
    }

    StrictContainer(Container&& container, Constraint constraint = {})
        : container_(std::move(container)), constraint_(constraint)
    {
        validateAll();
    }

    // container interface ...
    void push_back(const T& value)
    {
        if(!constraint_(value))
            throw WhateverException();
        container_.push_back(value);
    }

private:
    void validateAll()
    {
        for(const auto& value : container_)
        {
            if(!constraint_(value))
                throw WhateverException();
        }
    }

    Container container_;
    Constraint constraint_;
 };

然后您可以像这样实例化StrictContainer:

StrictContainer<int, std::vector<int>, IsEvenConstraint<int>> myContainer;

myContainer.push_back(2);
myContainer.push_back(18);
myContainer.push_back(7); // throws

在这种情况下,StrictContainer使用std :: vector作为数据存储,但您可以使用您喜欢的任何容器(如SimpleContainer)。然后可以像这样实现约束

template<typename T>
struct IsEvenConstraint
{
    bool operator()(const T& value)
    {
        return value % 2 == 0;
    }
};

根据您希望支持的容器和约束的类别,您可能必须相应地调整接口。使用可变参数模板,您可以扩展StrictContainer以支持多个约束。您也可以使用lambda作为约束。

auto lessThanOne = [](float f) { return f < 1.0f; };
StrictContainer<float, std::vector<float>, decltype(lessThanOne)> myContainer2(std::vector<float>{}, lessThanOne);
myContainer2.push_back(0.1f);
myContainer2.push_back(1.7f); // throws