- Expert C++
- Vardan Grigoryan Shunguang Wu
- 547字
- 2021-06-24 16:34:01
Copying objects
There are two kinds of copying: a deep copy and a shallow copy of objects. The language allows us to manage copy-initialization and the assignment of objects with the copy constructor and the assignment operator. This is a necessary feature for programmers because we can control the semantics of copying. Take a look at the following example:
Product p1;
Product p2;
p2.set_price(4.2);
p1 = p2; // p1 now has the same price
Product p3 = p2; // p3 has the same price
The line p1 = p2; is a call to the assignment operator, while the last line is a call to the copy constructor. The equals sign shouldn't confuse you regarding whether it's an assignment or a copy constructor call. Each time you see a declaration followed by an assignment, consider it a copy construction. The same applies to the new initializer syntax (Product p3{p2};).
The compiler will generate the following code:
Product p1;
Product p2;
Product_set_price(p2, 4.2);
operator=(p1, p2);
Product p3;
Product_copy_constructor(p3, p2);
The default implementation of the copy constructor (and assignment operator) performs a member-wise copy of objects, as shown in the following diagram:
Custom implementation is required in case the member-wise copy produces invalid copies. For example, consider the following copy of Warehouse objects:
class Warehouse {
public:
Warehouse()
: size_{0}, capacity_{1000}, products_{nullptr}
{
products_ = new Products[capacity_];
}
~Warehouse() {
delete [] products_;
}
public:
void add_product(const Product& p) {
if (size_ == capacity_) { /* resize */ }
products_[size_++] = p;
}
// other functions omitted for brevity
private:
int size_;
int capacity_;
Product* products_;
};
int main() {
Warehouse w1;
Product book;
Product apple;
// ...assign values to products (omitted for brevity)
w1.add_product(book);
Warehouse w2 = w1; // copy
w2.add_product(apple);
// something somewhere went wrong...
}
The preceding code declares two Warehouse objects, and two different products are then added to the warehouses. Though this example is somewhat unnatural, it shows the dangers of the default implementation of copying. The following illustration shows us what went wrong in the code:
Assigning w1 to w2 leads to the following structure:
The default implementation simply copies each member of w1 to w2. After copying, both products_ members of w1 and w2 point to the same location on the heap. When we add a new product to w2, the array pointed to by w1 is affected. It's a logical error that could lead to undefined behavior in the program. We need a deep rather than a shallow copy; that is, we need to actually create a new array of products that has a copy of w1's array.
A custom implementation of the copy constructor and the assignment operator solves this issue of shallow copying:
class Warehouse {
public:
// ...
Warehouse(const Warehouse& rhs) {
size_ = rhs.size_;
capacity_ = rhs.capacity_;
products_ = new Product[capacity_];
for (int ix = 0; ix < size_; ++ix) {
products_[ix] = rhs.products_[ix];
}
}
// code omitted for brevity
};
The custom implementation of the copy constructor creates a new array. Then, it copies the source objects' array elements one by one, this way eliminating the product_ pointer from pointing to a wrong memory address. In other words, we implemented a deep copy of Warehouse objects by creating a new array.
- 現代C++編程:從入門到實踐
- R語言數據分析從入門到精通
- Mastering AWS Lambda
- HTML5移動Web開發技術
- MySQL數據庫應用與管理 第2版
- AWS Serverless架構:使用AWS從傳統部署方式向Serverless架構遷移
- Python爬蟲開發:從入門到實戰(微課版)
- Internet of Things with Intel Galileo
- HTML5從入門到精通 (第2版)
- Android傳感器開發與智能設備案例實戰
- RocketMQ實戰與原理解析
- The Statistics and Calculus with Python Workshop
- ASP.NET開發寶典
- 程序員必會的40種算法
- SQL Server實例教程(2008版)