我有一个带有几层组件的应用程序。假设底部组件是A,并且顶部有组件B,C,D。
假设我在每个组件中都有一个方法,名称相同,例如' getMeSomething',从D调用.D调用C,C调用B,B调用A. 每个组件都有机会在调用其他组件之前做一些事情,并有机会对它得到的东西做一些回报(基于一些参数)。它甚至可以决定返回新的Something对象,而不是调用较低的组件。
所以,例如,当' D'组件调用方法在' C'组件:
// code from D class
Something s = c.getMeSomething(1)
在' C'组件就像这样处理:
Something C::getMeSomething(int arg)
{
if (arg == 1)
{
Log ("Hey, I've got 1");
Something sth = b.getMeSomething();
sth.remove(arg); // or whatever method on sth
}
else if (arg == 2)
{
return b.getMeSomething();
}
else
{
return Something(123); // whatever here
}
}
类似的事情发生在B和A中。
我关心的是按价值返回Something。 Something可能很大,我想确保它可以有效地传递,至少在这些情况下,在特定层中没有必要对这个Something对象进行任何操作(参见' arg == 2&#39的情况) ; 以上)。
例如,如果A返回Something而B返回同一个对象而不触及它,而C也只是将同一个对象返回给D,我想避免复制。我希望Something被移动,而不是被复制。
我如何确定在这种情况下使用移动?我可以吗?我该怎么做才能提供可移动性?
我想我应该将移动构造函数提供给Something类(或确保它可以由编译器自动生成)。我是对的吗?
但是当一个图层影响Something对象时会怎么样。这会影响整个情况,我的意思是Something刚刚传递到下一层的情况吗?
那么复制遗漏呢?编译器会使用这种技术而不是移动吗?这里的规则是什么?
一个更冗长的例子(为简单起见,有3个组件)。
#include <iostream>
#include <cassert>
struct Sth
{
int x;
};
struct A
{
Sth get(int i) { return Sth{333}; }
};
struct B
{
A a;
Sth get(int i)
{
if (i == 1)
return a.get(i);
else
{
Sth s = a.get(i);
s.x = 444;
return s;
}
}
};
struct C
{
B b;
Sth get(int i) { return b.get(i); }
};
int main()
{
C c;
Sth s1 = c.get(1);
assert (s1.x == 333);
Sth s2 = c.get(2);
assert (s2.x == 444);
}
答案 0 :(得分:0)
首先,总是通过引用返回。已经在评论中了。如果要改变,那就不要使它成为常数。虽然这是糟糕的设计,因为它会伤害封装。移动的东西当然有效,但移动有点听起来像是在这种情况下让对象不一致......但是,是的,取决于案例,不知道你的背景。
那说,还有另一种方法,只需控制Something的复制构造函数。
第一个解决方案:
Something(const Something& to_copy){
#if DEBUG
cout << "was copied!" << endl;
#endif
(more stuff)
}
也就是说,只需在打印消息时进行调试即可。当然,这需要您相信您的调试案例以涵盖所有情况,但与您所谈论的设计一样,这似乎是给出的。
第二个解决方案:
Something(const Something& to_copy) = delete;
只需删除复制构造函数即可。也就是说,也许你想要Something的对象一般是可复制的。然后去
class SomethingUncopyable : public Something {
SomethingUncopyable (const SomethingUncopyable & to_copy) = delete;
...
}
或者,如果你想保留一个班级,
class Something {
bool _copyable;
Something( arguments, const bool copyable = true) : _copyable(copyable) {
...
}
Something(const Something& to_copy){
assert(_copyable)
...
}
}
(我省略了可访问性修饰符)
答案 1 :(得分:0)
返回值时,您有几个不同的选项。您拥有的第一个选择是,如果您实际使用返回值,或者您通过引用的参数传递了您修改的参数。下一个选择是C ++为您提供的众多选项之一:
SomethingBig foo();
根据您的类和编译器的设计,这可能包括也可能不包括复制大量数据。
SomethingBig& foo();
void foo(SomethingBig&)
第一个包括思考你的对象所在的位置,即你不能像这样从函数范围返回对象。第二个版本将此责任传递给调用者。
SomethingBig* foo();
void foo(SomethingBig*);
指针快速复制,但你需要确保不引入内存泄漏,这对于原始指针来说可能很棘手,所以.......
std::unique_ptr<SomethingBig> foo();
void foo(std::unique_ptr<SomethingBig>&);
std::shared_ptr<SomethingBig> foo();
void foo(std::shared_ptr<SomethingBig>&);
通过一点点开销,智能指针可确保正确删除对象。这里的第一个版本可能需要一些std::move()
,第二个和第四个版本要求调用者首先创建一个(可能是空的)智能指针。如果只有一个所有者,则使用unique_ptr
;如果可能有多个,则使用shared_ptr
。
如果以上都不能满足您的需求,您需要正确设计您的课程。
通过实施(或删除)复制/移动构造函数/作业以及可能的“外包”和#39; (智能)指针后面的大数据块您可以高度控制何时实际复制内容。如果你另外实施move-constructors / assigments SomethingBig(SomethingBig&&);
&amp; SomethingBig& operator=(SomethingBig&&);
您创建了一个无法复制但移动(因此使用std::move()
返回)的类,而不实际复制所有数据。