组合常量和非常量引用数据成员的单个类
Single class which combines const and nonconst reference data member
这里有一组C++类,它们实现了一种适配器模式:
#include <iostream>
class Cfoo
{
public:
explicit Cfoo(int i):i_(i){}
void SetI(int i){ i_ = i; }
int GetI()const{ return(i_); }
private:
int i_;
};
class CfooHolderConst
{
public:
explicit CfooHolderConst(const Cfoo& foo):foo_(foo){}
int GetI()const{ return( foo_.GetI() ); }
private:
const Cfoo& foo_;
};
class CfooHolderNonConst
{
public:
explicit CfooHolderNonConst(Cfoo& foo):foo_(foo){};
int GetI()const{ return( foo_.GetI() ); }
void SetI(int i){ foo_.SetI(i); }
private:
Cfoo& foo_;
};
int main( int argc, char* argv[] )
{
const Cfoo myConstFoo(42);
CfooHolderConst myConstFooHolder(myConstFoo);
std::cout << myConstFooHolder.GetI() << std::endl;
Cfoo myNonConstFoo(1);
CfooHolderNonConst myNonConstFooHolder(myNonConstFoo);
myNonConstFooHolder.SetI(42);
std::cout << myConstFooHolder.GetI() << std::endl;
return(0);
}
我想将CfooHolderNonConst和CFooHolderConst组合成一个类,否则,从另一个类中继承一个。对Cfoo的引用在这里是一个问题,因为在CFooHolderConst中,它需要定义为const Cfoo&,而在CfooHolderNonConst中,它需要是Cfoo&。
这与此处的interator/cont_iterator问题类似:如何避免实现常量和非常量迭代器的代码重复?
但我希望,因为这不必满足STL迭代器的要求,所以可能会有一个更简单的解决方案。
在过去,我已经解决了这类问题,方法是将const和nonconst指针都作为类成员,并从重载构造函数中设置一个或另一个。这浪费了空间,看起来很笨拙。有没有更优雅的解决方案?
是的,可以做到:
template< typename T > CHolderReader
{
public:
explicit CHolderBase( T& t):t_(t){}
int Get()const { return t_.GetI(); }
protected:
~CHolderReader() {}
protected:
T& t_;
};
template< typename T > CHolderReaderWriter : public CHolderReader< T >
{
public:
void Set( int i)
{
t_.SetI(i);
}
};
typedef CHolderReader<const Cfoo> CFooHolderConst;
typedef CHolderReaderWriter<Cfoo> CFooHolderNonConst;
实际上,这是一个示例,您可以将底层数据的获取封装在其常量或非常量状态中。除非模板化的类型是const,否则Reader会保留一个非const引用,但不允许您对其进行写入,因此当您确实需要对其进行写操作时,可以像使用CHolderReaderWriter一样对其进行扩展。
您可以创建一个模板类,该类将为类的常量和非常量版本提供常量功能,然后继承以扩展具有修改成员功能的非常量版本:
class Cfoo
{
public:
explicit Cfoo(int i):i_(i){}
void SetI(int i){ i_ = i; }
int GetI()const{ return(i_); }
private:
int i_;
};
class CfooHolderNonConst;
template<class Foo>
class CFooHolder
{
friend class CfooHolderNonConst;
public:
explicit CFooHolder(Foo& foo):foo_(foo){}
int GetI()const{ return( foo_.GetI() ); }
private:
Foo& foo_;
};
typedef CFooHolder<const Cfoo> CfooHolderConst;
class CfooHolderNonConst: public CFooHolder<Cfoo>
{
public:
explicit CfooHolderNonConst(Cfoo& foo):CFooHolder(foo){};
void SetI(int i){ foo_.SetI(i); }
};
我认为将const和nonconst接口作为单独的类是个好主意。
它们为用户提供不同的接口,并具有不同的语义。在您的示例中,重复也是最小的。
如果你真的想拥有相同的类(提供仍然有意义的语义),那么我认为你想要这样的东西:
const Cfoo f1( 5 );
const CfooHolder h1( f1 );
Cfoo f2( 0 );
CfooHolder h2( f2 );
我认为您希望C++做出以下决定:a) 如果Cfoo对象是const
,则将其视为const
;如果它是非常数,则将它视为非常数。线索既包括Cfoo的定义,也包括CfooHolder。如果Cfoo是const
,则CfooHolder必须声明为const
,否则它将无法编译。如果Cfoo是非常量,那么您可以创建CfooHolder,它可以是const
和非const
。b) 方法SetI()
在const CfooHolder对象中使用时应停止编译。在上面的例子中,h1.SetI( 6 );
不应该编译。
我的答案是,如果a)起作用,那么b)也会自动起作用。问题是实现a),据我所知,这是不可能的。
为了实现这一点,应该在其类的对象的circunstance为const或nonconst的情况下,将属性设置为const
或non-const。尽管类的对象可以更改这种"状态",但是属性保持不变。但是,只有当此类的对象是const
时(例如,当参数通过常量引用传递时),才能使用const
方法。所以,C++不会支持这一点,因为它不是那样工作的。
另一种可能性是让属性本身同时为常量和非常量,这是没有意义的。
简短的回答是:这是不可能的,而且会有代码重复。如果你真的想避免这种情况,并且包装器足够复杂而令人担忧,那么唯一的方法就是创建一个通用保持器,然后在通用保持器周围创建常量和非常量包装器,避免重复到最低限度。
class CfooHolder
{
public:
explicit CfooHolder(Cfoo& foo):foo_(foo){};
int GetI()const{ return( foo_.GetI() ); }
virtual void SetI(int i){ foo_.SetI(i); }
protected:
Cfoo& foo_;
};
class CfooHolderNonConst : public CfooHolder {
public:
explicit CfooHolderNonConst(Cfoo& foo):CfooHolder(foo){};
};
class CfooHolderConst: public CfooHolder
{
public:
explicit CfooHolderConst(const Cfoo& foo):CfooHolder(const_cast<Cfoo &>( foo )){}
void SetI(int i){ throw std::runtime_error( "Don't write to me!" ); }
};
它并不完美,但它在规定的条件下工作。方法SetI()
会引发运行时错误,但如果CfooHolderConst对象被声明为const
,则对SetI()
的调用甚至不会编译。
希望这能有所帮助。
为什么不能只使用FooHolder
进行非常量(可变)访问,而使用const FooHolder
进行常量访问?
您不能在const对象上调用非const限定的方法(如SetI
),因此它似乎可以随心所欲。显然,您最初需要从一个非常常量Cfoo
创建holder对象。
示例:
class Cfoo
{
public:
explicit Cfoo(int i) : i_(i) {}
void SetI(int i) { i_ = i; }
int GetI() const { return(i_); }
private:
int i_;
};
class CfooHolder
{
public:
explicit CfooHolder(Cfoo& foo) : foo_(foo) {};
void SetI(int i) { foo_.SetI(i); }
int GetI() const { return( foo_.GetI() ); }
private:
Cfoo& foo_;
};
void bar(CfooHolder &holder, int i)
{
holder.SetI(i); // fine
}
void bar(CfooHolder const &constholder, int i)
{
holder.SetI(i);
// error: method exists, but I can't call it here
}
- 如何限制在C++中为单个类创建的对象数量?
- 模板化虚拟函数,管理单个类的不同类属性
- C++ 在多个其他类中使用单个类 - 编译时出现多个定义错误
- 包含 std::string 常量的类
- C++ 使用宏定义单个类的类成员
- __ getItem__对于单个类的多个属性
- 非常量引用类成员
- GCC C++覆盖单个类的-frtti
- SRP 是否与单个类的可能操作或其在其中的实现相关
- C++类型擦除,请使用 std::function 捕获单个类的多个方法
- 指针也可以指向常量空类或非常量类
- 组合常量和非常量引用数据成员的单个类
- 单个类存在类重新定义错误
- 在单个类中C++打印和反向打印的多态函数
- 查找c++中单个类的内存使用情况
- 单个类实例在GUI exe和dll中是不同的
- c++在多个进程之间共享单个类对象
- 在对象的二叉树中搜索单个类成员
- 如何使用类方法设置单个类数组元素
- 为什么我们不能有非常量的类级静态变量?