我有一个C ++类,它本质上是一个容器:
socket-accept
除了class SimpleContainer {
public:
// Various accessor methods / iterators
private:
// The actual content
}
之外 - 我想创建一个SimpleContainer
,它通过对内容强制执行规则来语义扩展StrictContainer
,但使用与现有{{1}相同的内容}实例。
SimpleContainer
现在 - 我的问题是SimpleContainer
和class 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 )
...
}
输入参数的内容吗?
答案 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)
所以,重申你的要求:
StrictContainer
与SimpleContainer
具有相同的界面,
添加。 (这意味着与 是 的关系
接口。)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