从常量引用初始化非常量对象时防止复制 我现在对C++引用语义有点困惑。假设我有一个返回常量引用的类: class foo { private: std::map<int, int> stuff; public: const std::map<int, int>& getStuff() { return stuff; } }; class-foo { 私人: 地图资料; 公众: const std::map&getStuff() { 归还物品; } };
我的用法如下:从常量引用初始化非常量对象时防止复制 我现在对C++引用语义有点困惑。假设我有一个返回常量引用的类: class foo { private: std::map<int, int> stuff; public: const std::map<int, int>& getStuff() { return stuff; } }; class-foo { 私人: 地图资料; 公众: const std::map&getStuff() { 归还物品; } };,c++,reference,constants,C++,Reference,Constants,我的用法如下: foo f; const std::map<int, int>& s = f.getStuff(); foo f; std::map<int, int> s = f.getStuff(); foof; const std::map&s=f.getStuff(); 这很好,但如果我按如下方式使用它: foo f; const std::map<int, int>& s = f.getStuff(); foo f; std:
foo f;
const std::map<int, int>& s = f.getStuff();
foo f;
std::map<int, int> s = f.getStuff();
foof;
const std::map&s=f.getStuff();
这很好,但如果我按如下方式使用它:
foo f;
const std::map<int, int>& s = f.getStuff();
foo f;
std::map<int, int> s = f.getStuff();
foof;
std::map s=f.getStuff();
到底发生了什么
如果我理解正确,则返回了对内容的常量引用,并在s
中创建了一个副本,我可以对其造成严重破坏。有什么办法可以避免这种情况吗
编辑:
因此,这里无法避免调用复制构造函数,因为std::map无论如何…std::map s=f.getStuff();
std::map<int, int> s = f.getStuff();
这将调用std::map
copy构造函数并创建对象的副本。stuff
映射的内容将复制到新映射s
您不能破坏原始对象,因为除了原始对象和新对象具有相同的内容之外,s
是一个与原始对象完全无关的新对象
通过foo::getStuff()
返回的const引用合法地破坏stuff
映射是不可能的。修改贴图的唯一方法是通过const\u cast
,通过const\u cast
获得的指针或引用修改对象可能会产生未定义的行为。是的,您的理解是正确的。这不是复制初始化的结果,而是使用复制构造函数。无法避免此副本,因为您所显示的代码段正在请求此副本
即使你破坏了这本书,也不要担心。原件仍然安全。问题是,只有在创建副本的过程可能造成严重破坏的情况下,这才是另一个问题
C++03相关参考文献:
$8.5/12-“初始化
在参数传递、函数中发生
返回,抛出异常(15.1),
处理异常(15.3),以及
括号内的初始值设定项列表
(8.5.1)称为复制初始化
与形式tx等价=
a、 "
$8.5/14-“如果初始化是
直接初始化,或者如果是
复制初始化,其中
cv源的不合格版本
类型与或类型是同一类
的派生类
目的地,构造函数是
已考虑。适用的
构造函数被枚举
(13.3.1.3),并选择最好的
通过过载分辨率(13.3)。
这样选择的构造函数称为
要初始化对象,请使用
初始值设定项表达式作为其
参数。如果没有构造函数
应用,或重载分辨率为
不明确,初始化是
格式不正确。”
是的,这将创建地图的副本
至于你的问题-取决于你想避免多少。一般来说,如果这是您自己声明的类,您可以将复制构造函数或运算符设置为私有以防止使用它,但显然,这将禁止您执行许多操作。简短回答:不,您不能阻止它。客户机不能修改原始地图,但如果您给客户机对地图的读取权限,那么客户机有责任不使用这些信息做愚蠢的事情;班级不可能阻止这一切
更长的回答:可能,但不是真的。如果您确实想使复制变得困难,可以将映射包装在一个带有私有复制构造函数和赋值运算符的类中。这样,s
赋值将是非法的(被编译器拒绝)。客户端仍然能够逐段读取映射的元素,并用它们填充一个新映射(手动副本),但防止这种情况发生的唯一方法是限制包装器类中的读取访问,哪种方法违背了getStuff
AFAIK的目的?复制构造函数将被调用,我看不到避免这种情况的方法。你是对的。但是,如果你想阻止自己这样做,你可以总是使s
const。同时,将s
设为常量ref意味着您需要确保f
在此期间保持活动状态——如果它是先前声明的自动变量,则这将自动发生,但是,如果它是在堆上创建的,则需要进行管理。但是,对s
执行的任何修改都不会影响原始对象stuff
。那么它是如何影响您的呢?与其返回对映射的引用,不如直接返回常量迭代器以开始()和结束()?问题解决了。不需要编写一堆包装器代码。@John:如果OP想要使用std::map
特定的功能,如{upper,lower}\u-bound
或find
,那么这不是一个真正有用的解决方案。因此,如果OP对引用语义有点困惑,引用该标准可能有点霸道……令人惊讶的是,这么多的选票,尽管它没有回答“有什么办法可以避免这种情况吗?”的问题。公平地说,我想它要么使用引用,要么去const std::map s=f.getStuff()代码>then@Chubsdad:不,我是在解决OP的元问题“这行代码到底是如何工作的?”一旦你发现原始对象是完整复制的,并且新对象除了具有相同的内容外,与原始对象完全无关,这应该很简单。@vic:对。调用代码是否可以修改它所创建的副本应由调用代码决定。如果调用方希望调用foo::getStuff()
,复制映射,并破坏该副本,foo
类