在C ++中,我经常面临这样一种情况:我需要编写const和非const版本的类,类似于const_iterator和标准库中的迭代器。
class const_MyClass
{
public:
const_MyClass(const int * arr):
m_arr(arr)
{
}
int method() const; //does something with m_arr without modifying it
private:
const int * m_arr;
}
class MyClass
{
public:
MyClass(int * arr):
m_arr(arr)
{
}
int method() const; //does something with m_arr without modifying it
void modify(int i); //modify m_arr
private:
int * m_arr;
}
这个问题是我需要在const_MyClass
中重复MyClass
的整个代码,并将API中的任何更改分发给这两个类。因此,有时候我会继承const_MyClass
并做一些const_casts,这也不是完美的解决方案。仍然当我想通过引用传递const_MyClass
实例时,它看起来很蠢:
void func(const const_MyClass & param)
实例参数标有两个" consts",它只有const方法......
这是const构造函数的用武之地,但是有没有现有的替代方案?
有些人用例子来更好地解释问题:
//ok to modify data
void f(int * data)
{
MyClass my(data);
my.modify();
...
}
//cant modify data, cant use MyClass
void fc(const int * data)
{
const_MyClass my(data);
int i = my.method();
...
}
答案 0 :(得分:5)
您可以将模板类作为基础,如下所示:
template<typename T>
class basic_MyClass
{
public:
basic_MyClass(T * arr) :m_arr(arr) {}
int method() const; //does something with m_arr without modifying it
private:
T * m_arr;
};
然后,对于你的const版本,因为它没有添加任何东西,你可以使用typedef
:
typedef basic_MyClass<const int> const_MyClass;
对于非const版本,您可以继承:
class MyClass : public basic_MyClass<int>
{
public:
using basic_MyClass::basic_MyClass; // inherit all the constructors
void modify(int i); //modify m_arr
};
答案 1 :(得分:1)
在没有可变值的情况下,您是否考虑过跟踪两个指针并从可变操作中引发异常?也许一个例子将有助于描述我的想法。
class MyClass
{
public:
MyClass(int *mutable_data):
m_mutable_view(mutable_data), m_readonly_view(mutable_data)
{
}
MyClass(const int *immutable_data):
m_mutable_view(NULL), m_readonly_view(immutable_data)
{
}
int retrieve_value(int index) {
return m_readonly_view[index];
}
void set_value(int index, int value) {
require_mutable();
m_mutable_view[index] = value;
}
protected:
void require_mutable() {
throw std::runtime_error("immutable view not available");
}
private:
const int *m_readonly_view;
int *m_mutable_view;
};
这里的想法非常简单 - 使用sentinel值来指示是否可以进行修改,而不是依赖于类型系统来为您执行此操作。就个人而言,我会考虑做@BenjaminLindley建议的inheritance based approach,但我想提出一个可能没有发生过的不同解决方案。
答案 2 :(得分:0)
在与Neil Kirk交谈后,我意识到我做错了什么。我开始将数据与逻辑分开,如他所建议的那样。
此尝试产生了两个类MyClassPtr
和const_MyClassPtr
。它们只提供数据访问功能(如迭代器),可能看起来像这样:
class const_MyClassPtr
{
public:
const_MyClassPtr(const int * arr);
int operator [](int i) const;
const int * ptr() const;
private:
const int * m_arr;
}
class MyClassPtr
{
public:
MyClassPtr(int * arr);
int operator [](int i) const;
int & operator [](int i);
const int * ptr() const;
int * ptr();
//promotion to const pointer
const_MyClassPtr () const {return const_MyClassPtr(m_arr);}
private:
int * m_arr;
}
现在很明显,这些类的对象应该像指针一样对待,所以当我将它们用作函数参数时,我会按值传递它们!
void func(const_MyClassPtr param) //instead of void func(const const_MyClass & param)
提供我创建MyClassOp
类模板和使用静态多态的方法。
template <class DERIVED>
class MyClassOp
{
public:
const DERIVED & derived() const {return static_cast<const DERIVED &>(*this)}
DERIVED & derived() {return static_cast<DERIVED &>(*this)}
int method() const; //operates on derived() const
void modify(int i); //operates on derived()
}
MyClassOp
是方法的集合。它没有国家。一般来说,它是trait。为了使这些方法可访问,我重载了->
和*
运算符
class const_MyClassPtr : private MyClassOp<const_MyClassPtr>
{
public:
const MyClassOp<MyClassPtr> * operator ->() const {return this;}
const MyClassOp<MyClassPtr> & operator *() const {return *this;}
...
}
class MyClassPtr : private MyClassOp<MyClassPtr>
{
public:
MyClassOp<MyClassPtr> * operator ->() {return this;}
MyClassOp<MyClassPtr> & operator *() {return *this;}
...
}
这适用于O.K.,但有点麻烦。如果我有例如相等运算符,我需要编写类似*myptr1 == myptr2
的内容来比较两个MyClassPtr
对象保存的值(它很容易出错并比较myptr1 == myptr2
或期望像*myptr1 == *myptr2
这样的东西可以工作)。当我分配类型时:
class MyClass : public MyClassOp<MyClass>
{
MyClass(int x, int y, int z);
...
int m_arr[3];
}
我希望能够将temporaries用作函数参数。
void f(const_MyClassPtr my);
//use temporary when calling f()
f(MyClass(1, 2, 3));
我可以通过提供转换运算符或转换构造函数(将MyClass
转换为const_MyClassPtr
)来实现此目的。但是const_MyClassPtr
的行为更像是引用而不是指针。如果迭代器是指针的泛化那么为什么一个人不能模仿引用呢?因此,我将MyClassOp
分为两部分(const和非const),并将->
和*
实现的const_MyClassPtr
和MyClassPtr
运算符替换为公共继承并更改了名称重新组合参考。我最终得到了以下结构。
MyClassOp : public const_MyClassOp
const_MyClassRef : public const_MyClassOp<const_MyClassRef>
MyClassRef : public MyClassOp<MyClassRef>
MyClass : public MyClassOp<MyClass>
然而const_MyClassRef
和MyClassRef
并不是完美的引用泛化,因为无法模仿某些C ++引用属性,因此Ref
后缀表示类似引用的结构。
答案 3 :(得分:-1)
也许你可以在有效的c ++中找到一些提示第4项&#34;避免在const和非const成员函数中重复&#34;
我可能会总结如下(即使使用有点丑陋的演员,也可以避免代码重复):
struct my_class
{
my_class(int x):_x(x){};
const int& method(void) const;
int& method(void);
int _x;
};
const int& my_class::method(void) const //func for const instance
{
return _x;
}
int& my_class::method(void) //func for normal instance
{
return const_cast<int& >(static_cast<const my_class& >(*this).method()) ;
}
int main()
{
my_class a(1);
const my_class b(2);
a.method() = 5;
cout << a.method() << endl;
//b.method() = 4; //b is const, wont compile
cout << b.method() << endl;
return 0;
}