c++ - How to safely copy structs with members pointing to other nested members in C++
问题描述
Consider these structs that come from an external library (I can't edit it)
struct Color;
struct Tires;
struct CarEF { // a car's external features
Color* pColor;
Tires* pTires;
};
Now, in my codebase, I want to use these structs and pass them to a factory-function that utilizes this struct to call a bunch of library constructors and create the Car
(and dependent) objects. To consolidate all the info, I made this struct.
struct CarInfo { // captures all info needed to create 'Car'
Color color{};
Tires {};
CarEF car_ef{};
// other items
};
CarInfo create_car_info() {
CarInfo info {
.color = { /* */ },
.tires = { /* */ },
.car_ef = {
.pColor = &info.color;
.pTires = &info.tires;
}
};
return info;
}
// factory function
Car carfactory(CarInfo) {
// create tire, create color ... etc (library code)
// call Car-ctor with the above (library code)
}
CarInfo create_info = create_car_info(); // assume copy
The pointers in the CarInfo
always point to other initialized members in the same struct instance.
Now, I'm afraid that the pointers might become invalid after the struct is copied. Sure, copy-elision could save me (will it?), but the question is, how do I safely copy this struct, so that the pointers in the create_info
point to the correct items?
info (local
stack variable
destroyed) create_info (copied)
+----------------+ +----------------+
| | | |
| +--->.color <-+-----+-+ .color |
| | | | | |
| | | | | |
| | +->.tyre <--+-----+-+-+ .tyre |
| | | | | | | |
| | | | | | | |
| | | .car | | | | .car |
| +-+----.pColor | | +-+----.pColor |
| | | | | |
| +----.pTyre | | +----.pTyre |
| | | |
+----------------+ +----------------+
解决方案
在您当前的代码中,create_car_info
返回一个命名的局部变量。允许编译器,但不需要在返回CarInfo
. 这称为命名返回值优化 (NRVO)
使用 C++17 和 20,如果您返回一个 pr 值,您将可以保证不会发生复制或移动,如下所示(关于此主题的 cpp 参考页):
CarInfo create_car_info() {
// assuming there is a ctor which will set up car_ef to point to the right addresses.
return CarInfo({/* color */}, {/* tires */});
}
您按值carfactory
取值CarInfo
,因此在这里制作了一个副本。您可以使用 const 引用来防止复制。
但是,从软件架构的角度来看,我建议不要依赖它。如果在任何时候,任何人决定只改变create_car_info
一点点,它就会严重破裂。
似乎您在这里添加自定义复制构造函数的唯一动机是确保复制操作后指针仍然正确。从cppreference 上所述的零规则:
具有自定义析构函数、复制/移动构造函数或复制/移动赋值运算符的类应专门处理所有权(遵循单一责任原则)。
所以,我建议你做的是找到一种方法,你不必担心你的指针,即使在传递你CarInfo
的值之后也是如此。我可以想到两种方法来实现这一点:
- 通过指针(最有可能作为智能指针)传递
CarInfo
对象,以确保对象不会在内存中移动,并且其内部指针在其整个生命周期内保持有效。然后,将它传递给函数时不需要复制它。 - 只需将一个成员函数添加到您的
CarInfo
结构中,即可为其在内存中的当前位置提供正确的指针。即使对象是按值传递的,因此制作了一个副本,并且使用它的代码向其副本询问指针,它也会返回正确的指针。
这些方法中的任何一种对您有用吗?如果不是:为什么?
推荐阅读
- django - 有条件的相关对象的返回值
- android - 如何在 setongoing(true) 之后删除持久通知?
- jmeter - Jmeter 为 SAP 应用程序进行负载测试时出现 500 内部服务器错误
- jquery - 相当于 jquery 对象 .get() 方法
- google-cloud-platform - gsutil rsync:转移的金额与“du”不同
- amazon-web-services - 在 Amazon Copy 期间未找到 DELIMITER
- sql - 如何在学生表中选择一个科目的最高分和另一科目的最低分?
- python - 使用 cv2 调整图像大小
- c# - 在 C# 中使用转置进行复制和粘贴
- python - 为什么 MSSQL 不向 CherryPy 返回数据?