我现在对C ++参考语义感到有点困惑。假设我有一个返回const引用的类:
class foo
{
private:
std::map<int, int> stuff;
public:
const std::map<int, int>& getStuff()
{
return stuff;
}
};
我用它如下:
foo f;
const std::map<int, int>& s = f.getStuff();
这很好,但如果我按如下方式使用它:
foo f;
std::map<int, int> s = f.getStuff();
到底发生了什么?
如果我理解正确的话,会返回对stuff
的const引用,并将副本创建到s
,我可以对其造成严重破坏。有没有办法避免这种情况?
编辑:
所以没有办法避免在这里调用复制构造函数,对于std :: map反正......
答案 0 :(得分:5)
简短回答:不,你无法阻止它。客户端不能修改原始文件,但是如果你给客户端读取地图的访问权限,那么客户端负责不用信息做蠢事;班级不可能阻止这一点。
更长的答案:也许,但不是真的。如果您确实想要复制困难,可以将地图包装在具有私有复制构造函数和赋值运算符的类中。这样s
赋值将是非法的(编译器拒绝)。客户端仍然能够零散地读取地图元素并用它们填充新地图 - 手动复制 - 但防止这种情况的唯一方法是限制包装类中的读取访问权限,哪种类型挫败了getStuff
的目的。
答案 1 :(得分:4)
std::map<int, int> s = f.getStuff();
这会调用std::map<int, int>
复制构造函数并复制该对象。 stuff
地图的内容将复制到新地图s
。
你不能对原始对象造成严重破坏,因为s
是一个与原始对象完全无关的新对象,除了原始对象和新对象具有相同的内容这一事实。
通过stuff
返回的const引用合法地破坏foo::getStuff()
地图是不可能的。修改地图的唯一方法是通过const_cast
,通过const_cast
获得的指针或引用修改对象可能会产生未定义的行为。
答案 2 :(得分:1)
是的,您的理解是正确的。这不是复制初始化,而是涉及使用复制构造函数。无法避免此副本,因为这是您显示的代码段所要求的内容。
即使您对副本造成严重破坏,也不要担心。原件仍然安全。只有在创建副本的过程可能造成严重破坏时才会引起关注,但那是一个不同的问题。
C ++ 03相关参考文献:
$ 8.5 / 12-“初始化 发生在参数传递,函数中 返回,抛出异常(15.1), 处理异常(15.3),和 大括号括起初始化列表 (8.5.1)称为复制初始化 并且相当于形式T x = 一个;“
$ 8.5 / 14-“如果初始化是 直接初始化,或者如果是 复制初始化的地方 cv-不合格的源版本 type是和a相同的类 派生类,类的 目的地,建设者是 考虑。适用 构造函数是枚举的 (13.3.1.3),选择最好的一个 通过重载决议(13.3)。 调用所选的构造函数 初始化对象,用 初始化表达式为 参数(一个或多个)。如果没有构造函数 适用,或重载决议是 暧昧,初始化是 病态的“。
答案 3 :(得分:0)
是的,这将创建地图的副本。
至于你的问题 - 取决于你想要避免多少。一般来说,如果这是你自己声明的一个类,你可以使复制构造函数或operator =
私有以防止它被使用,但很明显,这会禁止你做很多事情。