我正在尝试用C ++创建一个不可变类型(类),
我这样做是为了让所有方法“aka成员函数”不修改对象并返回一个新实例。
我遇到了很多问题,但它们都围绕着C ++中的引用类型。
一个例子是通过引用传递相同类类型的参数:
Imm Imm::someOp( const Imm& p_im ) const
{
...
p_im = p_im.someOtherOp(); //error, p_im is const, can't modify it!
...
}
错误是通过引用传递值引起的。 相反,我是通过值传递引用,然后上面的错误行不会是一个错误!
考虑Java / C#示例
class Imm
{
...
Imm someOp( Imm p_im )
{
....
p_im = p_im.someOtherOp(); //ok, you're not modifying the
//original object, just changing the local reference
....
}
....
}
如何在C ++中执行此类操作?我知道我可以使用指针,但后来我遇到了整个内存管理混乱。我不想担心谁拥有对象的引用。
理想情况下,我想将类设计为python中的不可变字符串;你可以使用它们而不会注意到甚至不知道它们是不可变的,它们只是按照你的期望行事;他们只是工作。
修改
当然,我可以通过传递值或使用临时变量来解决它(这是我目前正在做的事情)。我要问的是“如何通过C ++中的值传递引用”
我期待答案能够围绕STL中的某些内容展开,我目前正在研究smart_ptr系列模板。
更新
感谢您的回复,我意识到指针无法逃避。 (参见我的other question,这实际上就是对此的跟进)
答案 0 :(得分:4)
根据定义,赋值不是常量操作吗?
你看起来像是在尝试为const引用分配一些东西,这完全违背了const引用的想法。
我认为您可能正在寻找指针而不是参考。
答案 1 :(得分:4)
在Java和C#中,你并没有真正处理引用 - 它们更像是句柄或指针。 C ++中的引用实际上是原始对象的另一个名称,而不是指向它的指针(尽管它可以用指针实现)。为引用分配值时,您将分配给对象本身。令人困惑的是,初始化引用可以使用=
字符,但它是初始化,而不是赋值。
Imm im, im2, im3;
Imm &imr = im; // initialize a reference to im
imr = im2; // assign im2 to imr (changes the value of im as well)
Imm *imp = &im; // initialize a pointer to the address of im
imp = &im3; // assign the address of im3 to imp (im is unnaffected);
(*imp) = im2; // assign im2 to imp (modifies im3 as well).
如果您特别希望传递“按价值引用”,那么您实际上就是要求提出一个矛盾。根据定义,参考文献通过引用传递。正如其他地方所指出的,您可以按值传递指针,或者直接传递指针值。如果你真的想要,你可以在类中保留一个引用并按值传递它:
struct ImmRef
{
Imm &Ref;
ImmRef(Imm &ref) : Ref(ref) {}
};
另请注意,应用于引用的const使得引用的对象不变,而不是引用。引用总是const。
答案 2 :(得分:3)
它在C ++中不起作用。
当您传递对象的引用时,实际上是在对象的内存中传递地址。引用也不能重新定位到其他对象,因此C ++格言“引用是对象”。您必须制作副本才能对其进行修改。 Java将在幕后为您完成此操作。 C ++,你只需要复制它。
答案 3 :(得分:1)
您是否忘记将您调用的方法设置为const?
编辑:所以,修复了const。
也许你应该做像
这样的事情Imm & tmp = p_im.someOtherOp();
然后对tmp变量进行进一步操作。
如果将变量或参数设置为const&你无法分配它。
答案 4 :(得分:0)
您需要制作传入参数的新副本。你可以用几种等价的方式做你想做的事:1)你可以按值传递:
Imm Imm::someOp( Imm im ) const {
im = im.someOtherOp(); // local im is a copy, original im not modified
return im; // return by value (another copy)
}
或2)您可以通过引用传递并明确地制作副本:
Imm Imm::someOp( const Imm & im ) const {
Imm tmp = im.someOtherOp(); // local tmp is a copy
return tmp; // return by value (another copy)
}
两种形式都是等同的。
答案 5 :(得分:0)
选中此项以了解临时生命http://herbsutter.wordpress.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
Imm Imm::someOp( const Imm& p_im ) const
{
...
//Imm& im = p_im.someOtherOp(); // will *not* work
const Imm& im = p_im.someOtherOp(); // will work, but you get a const reference
...
}
但你可以使用boost :: shared_ptr
shared_ptr<Imm> Imm::someOtherOp() const
{
shared_ptr<Imm> ret = new Imm;
...
return ret;
}
shared_ptr<Imm> Imm::someOp(const share_ptr<Imm>& p_im) const
{
shared_ptr<Imm> im = p_im->someOtherOp();
}
答案 6 :(得分:0)
C ++比不可变类型(const
)要好。单个类型可以是可变的,也可以根据您的需要而可变。也就是说,有一些有用的模式可以处理短寿命(近)副本:
void f(const X &x) {
// Trivial case: unconditional copy
X x2=transform(x);
// Less trivial: conditional copy
std::optional<X> maybe;
const X &use=need_copy ? maybe.emplace(transform(x)) : x;
use.go(); // whichever, x or *maybe
} // *maybe destroyed iff created, then x2 destroyed
std::unique_ptr
可以在C ++ 17之前以类似的方式使用,尽管该函数当然可以抛出std::bad_alloc
。