Chapter.13 Copy Constructor
1、The Copy Constructor
What is a copy constructor? When is it used?
A copy constructor is a constructor which first parameter is a reference to the class type and any additional parameters have
default values.
When copy initialization happens and that copy initialization requires either the copy constructor or the move constructor
- Define variables using an
=
- Pass an object as an argument to a parameter of non-reference type
- Return an object from a function that has a non-reference return type
- Brace initialize the elements in an array or the members of an aggregate class
- Some class types also use copy initialization for the objects they allocate.
Assuming Point is a class type with a public copy constructor, identify each use of the copy constructor in this program fragment:
Point global;
Point foo_bar(Point arg) // 1
{Point local = arg, *heap = new Point(global); // 2, 3*heap = local;Point pa[ 4 ] = { local, *heap }; // 4, 5return *heap; // 6
}
2、The Copy-Assignment Operator
What is a
copy-assignment
operator? When is this operator used? What does the synthesizedcopy-assignment
operator do? When is it synthesized?
The copy-assignment
operator is function named operator=
.This operator is used when assignment occurred.The synthesized copy-assignment
operator assigns each non-static member of the right-hand object to corresponding member of the left-hand object using the copy-assignment
operator for the type of that member.It is synthesized when the class does not define its own
3、The Destructor
What is a destructor? What does the synthesized destructor do? When is a destructor synthesized?
The destructor is a member function with the name of the class prefixed by a tilde(~
).As with the copy constructor and the copy-assignment operator, for some classes, the synthesized destructor is defined to disallow objects of the type from being destroyed. Otherwise, the synthesized destructor has an empty function body.
The compiler defines a synthesized destructor for any class that does not define its own destructor.Just as a constructor has an initialization part and a function body (§ 7.5.1, p. 288), a destructor has a function body and a destruction part. In a constructor, members are initialized before the function body is executed, and members are initialized in the same order as they appear in the class. In a destructor,the function body is executed first and then the members are destroyed. Members are destroyed in reverse orderfrom the order in which they were initialized
In a destructor, there is nothing akin to the constructor initializer list to control how members are destroyed; the destruction part is implicit. What happens when a member is destroyed depends on the type of the member.Members of class type are destroyed by running the member’s own destructor. The built-in types do not have destructors, so nothing is done to destroy members of built-in typenote: The destructor is not run when a reference or a pointer to an object goes out of scope.
{// new scope// p and p2 point to dynamically allocated objectsSales_data *p = new Sales_data; // p is a built-in pointerauto p2 = make_shared<Sales_data>(); // p2 is a shared_ptrSales_data item(*p); // copy constructor copies *p into itemvector<Sales_data> vec; // local objectvec.push_back(*p2); // copies the object to which p2 pointsdelete p; // destructor called on the object pointed to by p
}// exit local scope; destructor called on item, p2, and vec// destroying p2 decrements its use count; if the count goes to 0, the object is freed// destroying vec destroys the elements in vec
#include <iostream>
#include <vector>
#include <initializer_list>struct X {X() { std::cout << "X()" << std::endl; } //1X(const X&) { std::cout << "X(const X&)" << std::endl; } //2X& operator=(const X&) //3{std::cout << "X& operator=(const X&)" << std::endl;return *this;}~X() { std::cout << "~X()" << std::endl; } //4
};void f(const X& rx, X x) // 2
{std::vector<X> vec; //omit skip the constructorvec.reserve(2); //omit skip the constructorX y = x; //2y=rx; //3vec.push_back(rx); //2vec.push_back(x); //2
}// destroy y, vec(twice), x //4 timesint main()
{X* px = new X; //1f(*px, *px);delete px; //4return 0;
}
The Rule of Three/Five
Classes That Need Destructors Need Copy and Assignment
Classes That Need Copy Need Assignment, and Vice Versa
Preventing Copies
The Destructor Should Not be a Deleted Member
reason:It is not possible to define an object or delete a pointer to a dynamically allocated object of a type with a deleted destructor
The Copy-Control Members May Be Synthesized as Deleted
1、The synthesized destructor is defined as deleted if the class has amember whose own destructor is deleted or is inaccessible (e.g., private).
2、The synthesized copy constructor is defined as deleted if the class has a member whose own copy constructor is deleted or inaccessible.It is also deleted if the class has a member with a deleted or inaccessible destructor.
3、The synthesized copy-assignment operator is defined as deleted if a member
has a deleted or inaccessible copy-assignment operator, or if the class has a
const or reference member.
4、The synthesized default constructoris defined as deleted if the class has a member with a deleted or inaccessible destructor;or has a reference member that does not have an in-class initializer (§ 2.6.1, p. 73);or has a const member whose type does not explicitly define a default constructor and that member does not have an in-class initializer
note:In essence, the copy-control members are synthesized as deleted when it is impossible to copy, assign, or destroy a member of the class
Swap
string *temp = v1.ps; // make a temporary copy of the pointer in v1.ps
v1.ps = v2.ps; // assign the pointer in v2.ps to v1.ps
v2.ps = temp; // assign the saved pointer in v1.ps to v2.ps
instead
void swap(HasPtr &lhs, HasPtr &rhs)
{using std::swap;swap(lhs.ps, rhs.ps); // swap the pointers, not the string dataswap(lhs.i, rhs.i); // swap the int members
}
swap Functions Should Call swap, Not std::swap
void swap(Foo &lhs, Foo &rhs)
{using std::swap;swap(lhs.h, rhs.h); // uses the HasPtr version of swap// swap other members of type Foo
}
Using swap in Assignment Operators
Assignment operators that use copy and swap are automatically exception safe and correctly handle self-assignment
#include <string>
#include <iostream>class HasPtr
{
public:friend void swap(HasPtr&, HasPtr&);friend bool operator<(const HasPtr &lhs, const HasPtr &rhs);HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { }HasPtr& operator=(HasPtr tmp) {this->swap(tmp);return *this;}~HasPtr() {delete ps;}void swap(HasPtr &rhs) {using std::swap;swap(ps, rhs.ps);swap(i, rhs.i);std::cout << "call swap(HasPtr &rhs)" << std::endl;}void show() const{ std::cout << *ps << std::endl; }
private:std::string *ps;int i;
};void swap(HasPtr& lhs, HasPtr& rhs)
{lhs.swap(rhs);
}bool operator<(const HasPtr &lhs, const HasPtr &rhs)
{return *lhs.ps < *rhs.ps;
}
Essentially, the specific avoiding memory allocation is the reason why it improve performance. As for the pointerlike version, no dynamic memory allocation anyway. Thus, a specific version for the valuelike will not improve the performance.
Swap
string *temp = v1.ps; // make a temporary copy of the pointer in v1.ps
v1.ps = v2.ps; // assign the pointer in v2.ps to v1.ps
v2.ps = temp; // assign the saved pointer in v1.ps to v2.ps
instead
void swap(HasPtr &lhs, HasPtr &rhs)
{using std::swap;swap(lhs.ps, rhs.ps); // swap the pointers, not the string dataswap(lhs.i, rhs.i); // swap the int members
}
swap Functions Should Call swap, Not std::swap
void swap(Foo &lhs, Foo &rhs)
{using std::swap;swap(lhs.h, rhs.h); // uses the HasPtr version of swap// swap other members of type Foo
}
Using swap in Assignment Operators
Assignment operators that use copy and swap are automatically exception safe and correctly handle self-assignment
#include <string>
#include <iostream>class HasPtr
{
public:friend void swap(HasPtr&, HasPtr&);friend bool operator<(const HasPtr &lhs, const HasPtr &rhs);HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { }HasPtr& operator=(HasPtr tmp) {this->swap(tmp);return *this;}~HasPtr() {delete ps;}void swap(HasPtr &rhs) {using std::swap;swap(ps, rhs.ps);swap(i, rhs.i);std::cout << "call swap(HasPtr &rhs)" << std::endl;}void show() const{ std::cout << *ps << std::endl; }
private:std::string *ps;int i;
};void swap(HasPtr& lhs, HasPtr& rhs)
{lhs.swap(rhs);
}bool operator<(const HasPtr &lhs, const HasPtr &rhs)
{return *lhs.ps < *rhs.ps;
}
Essentially, the specific avoiding memory allocation is the reason why it improve performance. As for the pointerlike version, no dynamic memory allocation anyway. Thus, a specific version for it will not improve the performance.
StrVec::StrVec(StrVec &&s) noexcept // move won't throw any
exceptions
// member initializers take over the resources in s
: elements(s.elements), first_free(s.first_free),
cap(s.cap)
{
// leave s in a state in which it is safe to run the destructor
s.elements = s.first_free = s.cap = nullptr;
}
allocate any resources. As a result, move operations ordinarily will not throw any
exceptions. When we write a move operation that cannot throw, we should inform the
library of that fact. As we’ll see, unless the library knows that our move constructor
won’t throw, it will do extra work to cater to the possibliity that moving an object of
our class type might throw.
note:Move constructors and move assignment operators that cannot throw exceptions should be marked as noexcept.
the library interacts with objects of the types we write. We need to indicate that a
move operation doesn’t throw because of two interrelated facts:
StrVec &StrVec::operator=(StrVec &&rhs) noexcept
{
// direct test for self-assignment
if (this != &rhs) {free(); // free existing elementselements = rhs.elements; // take over resources from rhsfirst_free = rhs.first_free;cap = rhs.cap;// leave rhs in a destructible staterhs.elements = rhs.first_free = rhs.cap = nullptr;}return *this;
}
Warning
After a move operation, the “moved-from” object must remain a valid, destructible object but users may make no assumptions about its value
The compiler synthesizes the move constructor and move assignment only if
a class does not define any of its own copy-control members and only if all
the data members can be moved constructed and move assigned,
respectively.
// the compiler will synthesize the move operations for X and hasX
struct X {int i; // built-in types can be movedstd::string s; // string defines its own move operations
};
struct hasX {X mem; // X has synthesized move operations
};X x, x2 = std::move(x); // uses the synthesized move constructorhasX hx, hx2 = std::move(hx); // uses the synthesized move constructor
class HasPtr {
public:HasPtr(const std::string &s = std::string()): ps(new std::string(s)), i(0) { }// each HasPtr has its own copy of the string to which ps pointsHasPtr(const HasPtr &p): ps(new std::string(*p.ps)), i(p.i) { }/*HasPtr& operator=(const HasPtr &rhs){auto newp = new string(*rhs.ps); // copy the underlying stringdelete ps; // free the old memoryps = newp; // copy data from rhs into this objecti = rhs.i;return *this; // return this object}*/~HasPtr() { delete ps; }// added move constructorHasPtr(HasPtr&& rhs) noexcept: ps(rhs.ps),i(rhs.i){rhs.ps = 0;}// assignment operator is both the move-and copy-assignment operatorHasPtr& operator=(HasPtr rhs){swap(*this,rhs);return *this;}
private:std::string *ps;int i;
};
hp = hp2; // hp2 is an lvalue; copy constructor used to copy hp2
hp = std::move(hp2); // move constructor moves hp2
In the second assignment, we invoke std::move to bind an rvalue reference to hp2. In this case,both the copy constructor and the move constructor are viable.However, because the argument is an rvalue reference, it is an exact match for the move constructor. The move constructor copies the pointer from hp2. It does not allocate any memory.
All five copy-control members should be thought of as a unit: Ordinarily, if a class defines any of these operations, it usually should define them all. As we’ve seen, some classes must define the copy constructor, copy-assignment operator, and destructor to work correctly . Such classes typically have a resource that the copy members must copy. Ordinarily, copying a resource entails some amount of overhead. Classes that define the move constructor and move-assignment operator can avoid this overhead in those circumstances where a copy isn’t necessary.
Chapter.13 Copy Constructor相关推荐
- C++ explicit constructor/copy constructor note
C++:explict 作用显示声明构造函数只能被显示调用从而阻止编译器的隐式转换,类似只能用()显示调用,而不能=或者隐式调用 1 #include <iostream> 2 #incl ...
- 关于c++中vector的push_back、拷贝构造copy constructor和移动构造move constructor
问题来自C++ Primer的第十三章练习题的13.48.是这样说的: 定义一个vector<String>并在其上多次调用push_back运行你的程序,并观察String被拷贝了多少次 ...
- no copy constructor available or copy constructor is declared #39;explicit#39;
今天新写了一个类.然后对这个类使用STL中的vector,碰到错误: no copy constructor available or copy constructor is declared 'ex ...
- halcon算子盘点:Chapter 13:对象、Chapter 14 区域
Chapter 13:Object 13.1 Information 1. count_obj 功能:统计一个元组中的对象. 2. get_channel_info 功能:一幅目标图像组成部分的信 ...
- 深度探索C++ 对象模型(4)-Default Copy Constructor(2)
没有Default Constructor, class Myclass{ public://... private:int a;char *str; }; 编译器执行的是"位逐次拷贝(Bi ...
- [C++]有关深复制与copy constructor的一些问题与实例
纸上得来终觉浅,绝知此事要躬行 --- 今天对此话有了实际的领悟.之前学习C++的时候,自以为已经把深复制和复制构造函数等这些知识已经掌握了,但真正写起项目来的时候,还是不能提前考虑这些问题,直到问题 ...
- Copy Constructor与赋值运算符
赋值运算符 '='的默认行为是数据成员的值的相应赋值,默认行为在类的成员中包含指针时容易出现问题,因为它只是赋值了指针的值,但是指针指向的值并未复制.赋值运算符是可以如同其他运算符一样重载的,重载后的 ...
- (原創) 哪些地方會用到Copy Constructor和Assignment Operator? (C/C++)
C#.Java都沒有copy constructor,所以這對大部分programmer都很陌生,簡單地說,凡需要copy的地方,就需要copy constructor: 1.由copy-initia ...
- C++ Copy Constructor (拷贝构造函数,复制构造函数)
1.什么是Copy Constructor? Copy Constructor 是一个特殊的构造函数,一般只有一个参数,这个参数一般是用const修饰的,对自己类的一个引用(reference).什么 ...
- 模拟集成电路笔记 | 第一部分 | Chapter 1-3
模拟集成电路笔记 | 第一部分 | Chapter 1-3 本系列笔记是参考书籍<CMOS模拟集成电路>和中科大相关课程课件而做成,笔记第一版为手写版,现在在手写版的基础上重新编写第二版( ...
最新文章
- Java Secret:使用枚举构建状态机
- java 调用对象的方法_JAVA调用对象方法的执行过程
- 线性最小二乘法的通俗理解
- html5输入框增加语音,为任意输入框添加语音输入功能
- 手里有200万,如何理财?
- ASPNET--Basic Info
- 传输表空间--使用Rman方式
- C#中提供了三种类型的计时器的比较实验(转自百度文库)
- 推荐一款颜值逆天且功能齐全的开源Shell工具
- 如何给multisim中导入9012/9013/8050/8550三极管
- linux联网是否,Linux命令 查看Linux版本和是否联网
- js基础-点击切换div背景颜色
- HTML---基础篇
- 【Flask】学习笔记 #12 —— JinJa2模板继承与引入
- 仿iphone顶部状态栏_无需第三方APP,苹果iPhone手机屏幕录制的方法
- Unable to load shared library ‘libgdiplus‘ or one of its dependencies
- Halcon入门教程手册
- 深入浅出C++ ——初识C++
- python3.7批量爬取百度图片/搜狗图片
- react根据中文获取拼音_解决 React 中的 input 输入框在中文输入法下的 bug