标题有点模糊,但我无法提出更好的措辞,这是交易:
class A
{
public:
A();
A( const PropertyB& b );
PropertyA GetPropertyA();
PropertyB GetPropertyB();
SetPropertyA( const PropertyA& b );
SetPropertyB( const PropertyB& b );
//assignment not allowed
};
假设我想使用std :: vector< A>,但是,如果它的所有元素具有相同的PropertyB值,则只有A的向量才有意义。当前的解决方案是提供类似控制器的方法来创建这样的数组,该方法保证返回数组的所有元素具有相同的PropertyB值,以及检查是否是这种情况的方法:
Array MakeArray( size_t, const PropertyB& );
bool CheckIfArrayIsSane( const Array& );
所以用户仍然可以在元素上调用SetPropertyB(),但有一个实用程序可以检查它,如果有人这样做就会挽救:
Array x( MakeArray( 3, someValue ) );
x.SetPropertyA( aaa ); //should be allowed
x.SetPropertyB( someOtherValue ); //should NOT be allowed
//somewhat further in the program
if( !CheckIfArrayIsSane( x ) )
throw InsaneArrayExcpetion();
虽然这很有效,但它容易出错,因为很难在任何地方强制执行此检查而不会忘记它,并且会使代码混乱并带有支票。
不工作的Approcahes:
一种更具侵入性的方法可行,但感觉有点不雅,需要额外的功能才能在两者之间进行转换等:
class AWithFixedPropertyB
{
public:
A( const PropertyB& b );
PropertyA GetPropertyA();
PropertyB GetPropertyB();
SetPropertyA( const PropertyA& b );
};
class A : public AWithFixedPropertyB
{
public:
//contrcutors etc
SetPropertyB( const PropertyB& b );
};
//ok, users cannot modify property B in this Array
typedef std::vector< AWithFixedPropertyB > Array;
这个问题最优雅的解决方案是什么?
答案 0 :(得分:2)
从OO设计的角度来看,这没有意义。还记得Liskov Susbsitution原则:只要A对象可以用在B对象的位置,A是-A B?根据该规则,您提议的std::vector<A>
的元素未通过is-an-A测试,因为您无法设置其B属性。然而,std::vector<A>
应该是A对象的容器。
在C ++中,我们有私有继承,这明确表示派生类不与其父类具有is-a关系。您可以按如下方式使用它:
class A_fixed_B : private A
{
A_fixed_B(A const& src) : A(src) {}
A const& asA() const { return *this; } // Base Conversion is OK inside class.
using A::GetPropertyA;
using A::GetPropertyB;
using A::SetPropertyA;
};
您现在可以创建一个std::vector<A_fixed_B>
,其行为与您可能期望的一样。
答案 1 :(得分:1)
我会说的:
class IA {
public:
virtual ~IA() {}
virtual PropertyA& GetPropertyA() = 0;
virtual void SetPropertyA(const PropertyA& a) = 0;
};
class A : public IA
{
public:
A();
A( const PropertyB& b );
PropertyA& GetPropertyA();
PropertyB GetPropertyB();
void SetPropertyA( const PropertyA& b );
void SetPropertyB( const PropertyB& b );
};
template< PropertyB value >
class fixedVector {
private:
std::vector<A> _tab;
public:
void pushback() {_tab.pushback(A(value)); }
IA& get(unsigned int i) { return _tab[i]; }
void popback() { _tab.pop_back(); }
};
如果您的A对象是由代理实例化的,您可以直接使用std::vector< std::autoptr<AI> >;
答案 2 :(得分:1)
更容易做的事情是包装std::vector<A>
并返回某种形式的代理对象。
class WrappedVectorA : private std::vector<A> {
struct MyProxy {
MyProxy(A& ref) { ptr = &ref; }
A* ptr;
PropertyA GetPropertyA() const { return ptr->GetPropertyA(); }
PropertyB GetPropertyB() const { return ptr->GetPropertyB(); }
SetPropertyA( const PropertyA& b ) { return ptr->SetPropertyA(b); }
operator=(const A& a) { *ptr = a; }
operator const A&() const { return *ptr; }
operator A() { return *ptr; }
};
public:
MyProxy operator[](int index) {
return std::vector<A>::operator[](index);
}
const MyProxy operator[](int index) const {
return const_cast<A&>(std::vector<A>::operator[](index));
}
};
这真的很难看,但它会起作用。我猜你还需要包装迭代器和at()
。但是,A
本身并不需要知道任何相关内容,而正常的std::vector<A>
完全不受影响。
答案 3 :(得分:1)
如果在分配后flyweight中元素的数量大于1,则使用flyweight存储PropertyB并在SetPropertyB中抛出异常。
无论您在哪个容器中存储As,都不允许使用不同的PropertyB。
#include <boost/flyweight.hpp>
class A
{
public:
A();
A( const PropertyB& b );
PropertyA GetPropertyA();
PropertyB GetPropertyB();
SetPropertyA( const PropertyA& b );
SetPropertyB( const PropertyB& b );
private:
PropertyA a;
boost::flyweights::flyweight<PropertyB> b;
};
A::SetPropertyB(const PropertyB& item)
{
b = item;
if(b.size() > 1) // you may have to implement flyweight::size() yourself
// should be able to do this based on core::factory().size();
throw InsaneArrayExcpetion();
}
实际上没有测试过这段代码的编译方式,但是它给了你一个想法。
答案 4 :(得分:0)
我会选择“Wrapping std :: vector&lt; A&gt; ...”方法。这样,类的用户将无法修改vector
中的对象,就好像他们想要调用SetPropertyB
一样,他们可以创建从此包装器返回的对象的副本并使用该方法
答案 5 :(得分:0)
使用策略基类来确定可以设置哪些字段 - 如果您尝试设置错误的属性,这将生成编译时错误。当然,缺点是A
不再仅仅是A
,而是在政策上输入。
代码:
#include <iostream>
#include <vector>
#include <boost/utility.hpp>
#include <boost/type_traits.hpp>
#include <boost/static_assert.hpp>
class AllPolicy
{
public:
void setA(int a) { _setA(a); }
void setB(int b) { _setB(b); }
private:
virtual void _setA(int a) = 0;
virtual void _setB(int b) = 0;
};
class APolicy
{
public:
void setA(int a) { _setA(a); }
typedef void setB;
private:
virtual void _setA(int a) = 0;
};
class BPolicy
{
public:
void setB(int b) { _setB(b); }
typedef void setA;
private:
virtual void _setB(int b) = 0;
};
template <typename Policy>
class A : public Policy
{
public:
A(int a = 0, int b = 0) : _a(a), _b(b)
{
}
A(A const& v) : _a(v._a), _b(v._b)
{
}
~A() {}
A& operator=(A const& v)
{
_a = v._a;
_b = v._b;
return *this;
}
int getA() const { return _a; }
int getB() const { return _b; }
using Policy::setA;
using Policy::setB;
private:
virtual void _setA(int a) { _a = a; }
virtual void _setB(int b) { _b = b; }
private:
int _a;
int _b;
};
int main(void)
{
std::vector<A<AllPolicy> > all_v(1, A<AllPolicy>(2, 3));
all_v[0].setA(1);
all_v[0].setB(2);
std::vector<A<APolicy> > a_v(1, A<APolicy>(2));
a_v[0].setA(1);
a_v[0].setB(2);
std::vector<A<BPolicy> > b_v(1, A<BPolicy>(1, 3));
b_v[0].setA(1);
b_v[0].setB(2);
}
演示:http://www.ideone.com/mTJSb
因此,我们的想法是使用继承,A
的基类将公开可以设置的内容。在AllPolicy
情况下,两种方法都被暴露,而在另一种情况下,暴露了一个或另一个设置器。如果您尝试使用与策略相反的setter,则应该产生编译器错误(如在演示中)。当然,现在A<APolicy>
与 A<BPolicy>
不同,如果您想要转换,则必须提供转换构造函数等。