C++;:通过'创建派生实例;扩展?';基本实例/实现向下转换? 我有一个C++类,它本质上是一个容器: class SimpleContainer { public: // Various accessor methods / iterators private: // The actual content }
除了C++;:通过'创建派生实例;扩展?';基本实例/实现向下转换? 我有一个C++类,它本质上是一个容器: class SimpleContainer { public: // Various accessor methods / iterators private: // The actual content },c++,C++,除了SimpleContainer,我还想创建一个StrictContainer,它通过对内容强制执行规则,在语义上扩展SimpleContainer,但使用与现有SimpleContainer实例相同的内容 class StrictContainer : public SimpleContainer { StrictContainer( const SimpleContainer& simple ) { // Check content in simple
SimpleContainer
,我还想创建一个StrictContainer
,它通过对内容强制执行规则,在语义上扩展SimpleContainer
,但使用与现有SimpleContainer
实例相同的内容
class StrictContainer : public SimpleContainer {
StrictContainer( const SimpleContainer& simple ) {
// Check content in simple, and raise an exception
// if it is not valid.
}
}
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_;
};
现在-我的问题是SimpleContainer
和StrictContainer
之间应该有什么样的关系:
is-a:
看起来很自然;这就是我在继承中指出的,但是接下来我将基于扩展SimpleContainer
实例后构造创建一个StrictContainer
实例-这可能吗
具有-a
或者,我可以将其实现为has-a关系,其中StrictContainer
有一个SimpleContainer
成员,但随后我需要在StrictContainer
中再次实现所有访问器和迭代器,并转发到SimpleContainer
实现
容器相当大;在创建StrictContainer
时,我不会复制内容。我想我想要的是一种实现我自己的downcast操作符的方法:
SimpleContainer simple;
// Fill simpleContainer
{
StrictContainer* strict = downcast<StrictContainer>( simple )
...
}
SimpleContainer simple;
//填充简单容器
{
StrictContainer*strict=downcast(简单)
...
}
如果
downcast
将调用StrictContainer
上的方法来验证简单输入参数的内容,那么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因此,重申您的要求:
- StrictContainer的接口与SimpleContainer的接口相同,
加上一些补充。(这意味着与
接口。)
- 插入新的
StrictContainer
不需要
复制SimpleContainer
内容。(这意味着使用句柄进行分解。)
假设原始的SimpleContainer
的生存期跨越StrictContainer
,这听起来像。所以,如果你愿意做一点分解——定义一个这样的接口怎么样
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.
};
不需要特殊的“downcast操作符”,因为您已经需要调用StrictContainer
的构造函数,并且它已经完成了所有需要的操作
SimpleContainer simple( ... ); // Construct as normal.
StrictContainer strict = simple; // Assuming no extra arguments needed.
很简单
是的,您需要在decorator类中为接口中的每个方法编写一次委托操作。但是您可以定义StrictContainer类的多个变体,而无需再次实现它们(除非您希望覆盖其中一些变体)。我更喜欢使用适配器和策略(策略)模式的组合来实现类似的内容。您可以将StrictContainer(适配器)与完全保存数据的底层容器分离。非常类似于std::queue作为另一个容器(如vector)的适配器实现的方式。然后,您可以使用您想要的任何约束参数化StrictContainer适配器。无论何时向容器中添加元素,都会检查约束是否满足。如果是,则将元素添加到基础容器中,否则可以执行任何您喜欢的操作(例如保持容器不变或引发异常)
模板
类容器
{
公众:
StrictContainer(const-Container&Container={},Constraint={})
:容器(容器),约束(约束)
{
validateAll();
}
StrictContainer(容器和容器,约束={})
:容器(std::move(container)),约束(constraint)
{
validateAll();
}
//容器接口。。。
无效推回(常数T和值)
{
如果(!约束(值))
抛出WhateverException();
容器向后推(值);
}
私人:
void validateAll()
{
用于(常量自动和值:容器)
{
如果(!约束(值))
抛出WhateverException();
}
}
集装箱;;
约束约束;
};
然后可以像下面这样实例化StrictContainer:
StrictContainer<int, std::vector<int>, IsEvenConstraint<int>> myContainer;
myContainer.push_back(2);
myContainer.push_back(18);
myContainer.push_back(7); // throws
StrictContainer;
myContainer.向后推(2);
myContainer.向后推(18);
myContainer.向后推(7);//投掷
在本例中,StrictContainer使用std::vector作为数据存储,但您可以使用您喜欢的任何容器(如SimpleContainer)。然后可以像这样实现约束
template<typename T>
struct IsEvenConstraint
{
bool operator()(const T& value)
{
return value % 2 == 0;
}
};
模板
结构Iseven约束
{
布尔运算符()(常量T和值)
{
返回值%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
auto-lessThanOne=[](float f){return f<1.0f;};
支原体2(std::vector{},lessThanOne);
支原体2.向后推(0.1f);
myContainer2.向后推(1.7f);//投掷
作为第三种选择,我建议采用基于策略的方法,如下例所示:
#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));
}
#包括
#包括
模板
结构NoCheck{
静态布尔isOk(T){
返回true;
}
};
模板
s