首先,我想指出这是我第一次使用动态多态和复合设计模式。
我想使用复合设计模式来创建一个class Tree
,它能够采用Tree
类型的不同对象,一种复合类型,或Leaf
,一种原子类型。 Tree和Leaf都继承自一个普通的类Nature
。树可以将叶子或树对象存储到std::vector<std::shared_ptr<Nature>> children
中。我想用这种语法填充矢量children
(所以我想我必须使用variadic,考虑输入列表中的通用输入数量),如下所示:
Leaf l0(0);
Leaf l1(1);
Tree t0;
Tree t1;
t0.add(l0,l1);
t1.add(t0,l0,l1); // or in general t1.add(t_00,...,t_0n, l_00,...,l_0n,t10,...,t1n,l10,...,l1n,.... )
然后我还将通过Tree
访问operator[ ]
的不同元素。所以例如t1[0]
返回t0
和t1[0][0]
返回l0
,而t1[0][1]
返回l0
。
我也想要一个同质的行为。因此,要么使用->
或点来访问所有级别(树或叶子)上的方法。
有可能实现这种行为吗?
这些类的实现可以如下所示:
class Nature
{
public:
virtual void nature_method() = 0;
virtual~Nature();
//virtual Nature& operator[] (int x);
};
class Leaf: public Nature
{
int value;
public:
Leaf(int val)
{
value = val;
}
void nature_method() override
{
std::cout << " Leaf=="<<value<<" ";
}
};
class Tree: public Nature
{
private:
std::vector <std::shared_ptr< Nature > > children;
int value;
public:
Tree(int val)
{
value = val;
}
void add(const Nature&);
void add(const Leaf& c)
{
children.push_back(std::make_shared<Leaf>(c));
}
void add(const Tree& c)
{
children.push_back(std::make_shared<Tree>(c));
}
void add(std::shared_ptr<Nature> c)
{
children.push_back(c);
}
template<typename...Args>
typename std::enable_if<0==sizeof...(Args), void>::type
add(const Leaf& t,Args...more)
{
children.push_back(std::make_shared<Leaf>(t));
};
template<typename...Args>
typename std::enable_if<0==sizeof...(Args), void>::type
add(const Tree& t,Args...more)
{
children.push_back(std::make_shared<Tree>(t));
};
template<typename...Args>
typename std::enable_if<0<sizeof...(Args), void>::type
add(const Leaf& t,Args...more)
{
children.push_back(std::make_shared<Leaf>(t));
add(more...);
};
template<typename...Args>
typename std::enable_if<0<sizeof...(Args), void>::type
add(const Tree& t,Args...more)
{
children.push_back(std::make_shared<Tree>(t));
add(more...);
};
void nature_method() override
{
std::cout << " Tree=="<< value;
for (int i = 0; i < children.size(); i++)
children[i]->nature_method();
}
}
我可以实现重载operator []
来返回指向Nature或Nature对象的指针,如下所示:
Nature& operator[] (int x) {
return *children[x];
}
std::shared_ptr< Nature > operator[] (int x) {
return children[x];
}
在这两种情况下,返回类型都与Nature
相关。这是因为它可能是一个Leaf
或Tree
,这是事先不知道的。但由于必须在编译时知道运算符的返回类型,我不能做其他事情。
但是,如果返回的类型与Tree
相关,我不能再使用operator []
,因为我已经强制它为Nature
。
如何动态选择Tree
的返回类型Leaf
或[]
?这有什么解决方法吗?
我可以认为operator []
是Nature类中的一个虚拟方法,但我仍然不知道如何解决这个问题。
我也读过有关协变类型的内容,但我不知道它们是否适用于此。
谢谢。
如果您想要类型安全,则必须在每个使用站点检查[]
的返回值,以确定它是Tree
还是Leaf
。
你也可以选择不是类型安全的,并且如果你以一种应该是Leaf
的方式使用Tree
,则调用未定义的行为。
而不管:
virtual Nature& operator[](std::ptrdiff_t i) {
throw std::invalid_argument("Not a Tree");
}
virtual Nature const& operator[](std::ptrdiff_t i) const {
throw std::invalid_argument("Not a Tree");
}
在Nature
,接着是:
virtual Nature& operator[](std::ptrdiff_t i) final override {
auto r = children.at((std::size_t)x);
if (r) return *r;
throw std::out_of_range("no element there");
}
virtual Nature const& operator[](std::ptrdiff_t i) const final override {
auto r = children.at((std::size_t)x);
if (r) return *r;
throw std::out_of_range("no element there");
}
在Tree
。
当你在错误的类型上使用[]
时,这会产生异常。