我班上有几个成员const
,因此只能通过初始化列表进行初始化,如下所示:
class MyItemT
{
public:
MyItemT(const MyPacketT& aMyPacket, const MyInfoT& aMyInfo)
: mMyPacket(aMyPacket),
mMyInfo(aMyInfo)
{
}
private:
const MyPacketT mMyPacket;
const MyInfoT mMyInfo;
};
我的类可以在我们的一些内部定义的容器类(例如向量)中使用,并且这些容器要求在类中定义operator=
。
当然,我的operator=
需要做这样的事情:
MyItemT&
MyItemT::operator=(const MyItemT& other)
{
mMyPacket = other.mPacket;
mMyInfo = other.mMyInfo;
return *this;
}
这当然不起作用,因为mMyPacket
和mMyInfo
是const
成员。
除了让这些成员不是const
(我不想做)之外,还有任何关于如何解决这个问题的想法?
答案 0 :(得分:7)
如果你有一个可以在构造完成后更改它们的赋值运算符,那么你违反了const的定义。如果你真的需要,我认为Potatoswatter的放置新方法可能是最好的,但是如果你有一个赋值运算符,你的变量实际上不是常量,因为有人可以创建一个新实例并用它来改变他们的价值观
答案 1 :(得分:3)
您可以存储指针(或智能指针),而不是直接在容器中存储对象。这样,您就不必改变您班级中的任何成员 - 您获得与传入的完全相同的对象,const
和所有成员。
当然,这样做可能会在一定程度上改变应用程序的内存管理,这可能是一个不想要的理由。
答案 2 :(得分:2)
这是一个肮脏的黑客,但你可以摧毁和重建自己:
MyItemT&
MyItemT::operator=(const MyItemT& other)
{
if ( this == &other ) return *this; // "suggested" by Herb Sutter ;v)
this->MyItemT::~MyItemT();
try {
new( this ) MyItemT( other );
} catch ( ... ) {
new( this ) MyItemT(); // nothrow
throw;
}
return *this;
}
编辑:以免破坏我的可信度,我实际上并不这样做,我会删除const
。但是,我一直在讨论改变这种做法,因为const
只是有用而且最好尽可能使用。
有时资源和对象表示的值之间存在区别。只要资源相同,成员就可以通过更改值来构造const,并且在此时获得编译时安全性会很好。
编辑2: @Charles Bailey提供了这个精彩(且非常关键)的链接:http://gotw.ca/gotw/023.htm。
operator=
中的语义都很棘手。operator&
重载(无论如何) 编辑3:通过“哪个资源”与“什么样的价值”区分进行思考,似乎operator=
应该始终更改值而不是资源。然后,资源标识符可以是const
。在示例中,所有成员都是const
。如果“信息”是存储在“数据包”中的内容,那么数据包可能是const
而信息不是。
因此,如果“信息”实际上是元数据,那么问题不在于将分配的语义理解为在此示例中缺少明显的值。如果任何类拥有MyItemT
想要将其从一个数据包切换到另一个数据包,它需要放弃并使用auto_ptr<MyItemT>
代替,或者采取与上述类似的黑客攻击(身份测试是不必要的)但剩下的catch
已从外部实施 。但是operator=
不应该改变资源绑定,除非作为一个绝对不会干扰其他任何内容的特殊功能。
请注意,此约定适用于Sutter关于在分配方面实施复制构造的建议。
MyItemT::MyItemT( MyItemT const &in )
: mMyPacket( in.mMyPacket ) // initialize resource, const member
{ *this = in; } // assign value, non-const, via sole assignment method
答案 3 :(得分:1)
您可以考虑将MyPacketT
和MyInfoT
成员指向const
(或指向const
的智能指针)。这样,数据本身仍然标记为const和immutable,但是如果有意义的话,你可以干净地“交换”到赋值中的另一组const数据。实际上,您可以使用交换习惯用法以异常安全的方式执行赋值。
因此,您可以获得const
的好处,以帮助您防止意外地允许您希望设计阻止的更改,但您仍然允许从另一个对象分配整个对象。例如,这将允许您在STL容器中使用此类的对象。
您可能会将此视为'pimpl'习语的特殊应用。
有些事情:
#include <algorithm> // for std::swap
#include "boost/scoped_ptr.hpp"
using namespace boost;
class MyPacketT {};
class MyInfoT {};
class MyItemT
{
public:
MyItemT(const MyPacketT& aMyPacket, const MyInfoT& aMyInfo)
: pMyPacket(new MyPacketT( aMyPacket)),
pMyInfo(new MyInfoT( aMyInfo))
{
}
MyItemT( MyItemT const& other)
: pMyPacket(new MyPacketT( *(other.pMyPacket))),
pMyInfo(new MyInfoT( *(other.pMyInfo)))
{
}
void swap( MyItemT& other)
{
pMyPacket.swap( other.pMyPacket);
pMyInfo.swap( other.pMyInfo);
}
MyItemT const& operator=( MyItemT const& rhs)
{
MyItemT tmp( rhs);
swap( tmp);
return *this;
}
private:
scoped_ptr<MyPacketT const> pMyPacket;
scoped_ptr<MyInfoT const> pMyInfo;
};
最后,我将我的示例更改为使用scoped_ptr<>
而不是shared_ptr<>
,因为我认为这是OP意图的更一般的表示。但是,如果可以共享“可重新分配的”const
成员(并且这可能是真的,因为我理解OP为什么需要他们const
),那么使用shared_ptr<>
可能是一种优化让shared_ptr<>
类的复制和赋值操作处理这些对象的事情 - 如果你没有其他成员需要特殊的复制或分配语义,那么你的课程就简单了很多,并且您甚至可以通过共享MyPacketT
和MyInfoT
对象的副本来节省大量内存使用量。
答案 4 :(得分:1)
我认为你可以使用特殊的const
代理。
template <class T>
class Const
{
public:
// Optimal way of returning, by value for built-in and by const& for user types
typedef boost::call_traits<T>::const_reference const_reference;
typedef boost::call_traits<T>::param_type param_type;
Const(): mData() {}
Const(param_type data): mData(data) {}
Const(const Const& rhs): mData(rhs.mData) {}
operator const_reference() const { return mData; }
void reset(param_type data) { mData = data; } // explicit
private:
Const& operator=(const Const&); // deactivated
T mData;
};
现在,您将拥有const MyPacketT
而不是Const<MyPacketT>
。并不是说接口只提供了一种改变它的方法:通过显式调用reset
。
我认为mMyPacket.reset
的任何使用都可以轻松搜索。正如@MSalters
所说,它可以防止墨菲,而不是马基雅维利:)