我正在寻找合适的数据模型。假设A是具有属性a的类,其具有简单的计算方法getat():
class A{
protected:
int a;
public:
int getat(int t){return f(a,t);} //Some calculation
virtual int getbt(int t)=0; //b defined in B, C
virtual int getct(int t)=0; //c defined in C
virtual ~A()=0;
};
类似地,B,C是具有属性b,c的派生类(但C不是B的类型):
class B{
protected:
int b;
public:
virtual int getbt(int t){return f(b,t);} //Some calculation
virtual int getct(int t){return f(b,t)};
virtual ~B(){};
};
class C{
protected:
int b, c;
public:
virtual int getbt(int t){return f(b,t)};
virtual int getct(int t){return g(c,t)};
virtual ~C(){};
};
考虑到多态性,我想使用父类A的存储对象:
std::list <A>; //Abstract class, impossible
std::list <A*>; //Possible, but problem with the destructors
std::list<std::shared_ptr<A> >; //The best solution?
我的问题:
1]建议的具有纯虚函数的数据模型是否合理?
2]删除抽象是否有意义?
class A{
protected:
int a;
public:
int getat(int t){return f(a,t);}
int getbt(int t){};
int getct(int t){};
}
随后,允许使用std :: list。
3]如果需要多态性,建议使用哪种方法存储A对象?
感谢您的帮助......
答案 0 :(得分:1)
为了完整性,我已将句柄设为可复制
#include <utility>
#include <memory>
#include <vector>
#include <iostream>
int f(int a, int t) {
return a * t;
}
struct has_a
{
int a;
};
/// when some T is not derived from has_a, its getat method will return 0
template<class T, std::enable_if_t<not std::is_base_of<has_a, T>::value>* = nullptr>
int impl_getat(const T&, int t) { return 0; }
// when some T is derived from has_a, its getat method will return f(a, t)
template<class T, std::enable_if_t<std::is_base_of<has_a, T>::value>* = nullptr>
int impl_getat(const T& a, int t) { return f(a.a, t); }
// ditto for has_b
struct has_b
{
int b;
};
template<class T, std::enable_if_t<not std::is_base_of<has_b, T>::value>* = nullptr>
int impl_getbt(const T&, int t) { return 0; }
template<class T, std::enable_if_t<std::is_base_of<has_b, T>::value>* = nullptr>
int impl_getbt(const T& b, int t) { return f(b.b, t); }
// ditto for has_c
struct has_c
{
int c;
};
template<class T, std::enable_if_t<not std::is_base_of<has_c, T>::value>* = nullptr>
int impl_getct(const T&, int t) { return 0; }
template<class T, std::enable_if_t<std::is_base_of<has_c, T>::value>* = nullptr>
int impl_getct(const T& c, int t) { return f(c.c, t); }
// an object to hold the polymorphic model
struct handle
{
// the concept that defines the operations on the model
struct concept {
// rule of 5 when virtual destructors are involved...
concept() = default;
concept(const concept&) = default;
concept(concept&&) = default;
concept& operator=(const concept&) = default;
concept& operator=(concept&&) = default;
virtual ~concept() = default;
// cloneable concept
virtual std::unique_ptr<concept> clone() const = 0;
// concept's interface
virtual int getat(int t) = 0;
virtual int getbt(int t) = 0;
virtual int getct(int t) = 0;
};
// a model models the concept, by deriving from any number of discrete parts
template<class...Parts>
struct model : concept, Parts...
{
model(Parts...parts)
: Parts(std::move(parts))...
{}
model(const model&) = default;
// model the clone op
std::unique_ptr<concept> clone() const override {
return std::make_unique<model>(*this);
}
// defer to impl functions (see above) for the calculations
int getat(int t) override { return impl_getat(*this, t); }
int getbt(int t) override { return impl_getbt(*this, t); }
int getct(int t) override { return impl_getct(*this, t); }
};
std::unique_ptr<concept> _impl;
// interface - note: not polymorphic, so we can be stored in a container
int getat(int t) { return _impl->getat(t); }
int getbt(int t) { return _impl->getbt(t); }
int getct(int t) { return _impl->getct(t); }
// constructor - construct from parts
template<class...Parts>
handle(Parts...parts)
: _impl(std::make_unique<model<std::decay_t<Parts>...>>(std::move(parts)...))
{
}
// let's make it copyable
handle(const handle& r)
: _impl(r._impl->clone())
{
}
// rule of 5 because we meddled with the copy constructor...
handle(handle&& r) : _impl(std::move(r._impl)) {}
handle& operator=(const handle& r) {
_impl = r._impl->clone();
return *this;
}
handle& operator=(handle&& r) = default;
};
int main()
{
std::vector<handle> v;
v.emplace_back(has_a{10}, has_b{12});
v.emplace_back(has_a{1}, has_b{2}, has_c {3});
int aa = 1;
for (auto& x : v)
{
std::cout << x.getat(aa) << std::endl;
std::cout << x.getbt(aa) << std::endl;
std::cout << x.getct(aa) << std::endl;
std::cout << std::endl;
++aa;
}
// prove it's copyable etc
auto y = v.back();
std::cout << y.getat(aa) << std::endl;
std::cout << y.getbt(aa) << std::endl;
std::cout << y.getct(aa++) << std::endl;
std::cout << std::endl;
// and moveable
y = handle(has_a{4}, has_b{5}, has_c{6});
std::cout << y.getat(aa) << std::endl;
std::cout << y.getbt(aa) << std::endl;
std::cout << y.getct(aa++) << std::endl;
std::cout << std::endl;
}
预期结果:
10
12
0
2
4
6
3
6
9
16
20
24
答案 1 :(得分:0)
如果默认实现在您的情况下没有意义,我更喜欢纯虚拟方法,这可以防止在编译时客户端滥用您的类,因此1考虑interface classes。 对于2,你应该注意到,如果你的客户没有覆盖什么是行为?运行时的消息,“请实现这个”...... 请详细说明3 ...