[C++ Primer] C13 Copy control(重点章节)

Chapter 13 Copy control

Aim: How classes can control what happens when the objects of the class type are copied, assigned, moved, or destroyed.

copy and move constructors define what happens when an object is initialized from another object of the same type.

copy and move assignment define what happens when we assign an object of a class type to another object of that same class type.

destructor defines what happens when an object of the type ceases to exist.

All these operations can be seen as copy control.

13.1 Copy, Assign, and Destroy

copy constructor

The first parameter is a reference to the class type and any additional paramerters have default values:

class Foo{
public:
    Foo(); // default construtor
    Foo(const Foo& a); // copy constructor
};
copy initialization
Copy initialization happens not only when we define variables using an =, but also
when we:
    1. pass an object as an argument to a parameter of nonreference type
    2. return an object from a function that has a nonreference return type
    3. brace initialize the elements in an array or the members of an aggregate class

string s(10, '9') // direct initialization
string s1(s); // copy initialization
string s2 = s; // copy initialization
string s3 = "00000"; // copy initialization
string s4 = string(10, '9'); // copy initialization
  • what is the copy constructor, and when is it used? A copy constructor is used when we want to create a new object that is a copy of an existing object.

copy assignment

  1. overloaded operators: functions that have the name operator followed by the symbol for the operator being defined. Hence the assignment operator is a function named operator=, it has a return type and parameter list.
class A {
public:
    A() {};
    A(const A& a) {
        this.b = a.b;
        this.c = a.c;
    }
    A& operator=(const A& rhs) {
        b = rhs.b;
        c = rhs.c;
        return *this;
    }

private:
    int b;
    string c;
}

int main() {
    return 0;
}

destructor

  • The destructor operates inversely to the constructors: Constructors initialize the nonstatic data members of an object and may do other work; destructors do whatever work is needed to free the resources used by an object and destroy the nonstatic data members of the object
class A{
public:
    A(){};
    ~A(); //destructor
};

The implicit destruction of a member of built-in pointer type does not delete the object to which that pointer points.

For example, if a class A contains a member int* ptr, and an object of class A is destroyed, the memory for the int pointed to by ptr is not automatically released. This can lead to memory leaks if the memory allocated to ptr is not manually deallocated before the object is destroyed.

To avoid memory leaks, it is important to properly manage the lifetime of dynamically allocated memory, such as by using smart pointers or manually deallocating the memory in the destructor of the class.

The rule of three/five

There are three basic operations to control copies of class objects: The copy constructor, copy-assignment operator, and destructor.

rule of 5:
    1. copy constructor
    2. copy assignment operator
    3. destructor
    4. move constructor
    5. move assignment operator
class A{
public:
    A(){};
    A(const A& a) {
        this.a = a.a;
        this.b = a.b;
        cout<< "copy constructor";
    }
    A& operator=(const A& a) {
        this.a = a.a;
        this,b = a.b;
        return this*;
        cout<< "copy-assignment operator";
    }
    ~A(){
        cout<< "destructor";
    }

private:
    int a;
    string b;
};

using default =

  • Classes that want to prevent copying should define their copy constructor and copy-assignment operators using = delete rather than making those members private.
class A{
    A() = default; // use the synthesized default constructor
    A(const A& noCopy) = delete; // no copy
    A& operator=(const A& noAssignment) = delete; // no assignment
    ~A() = default; // use the synthesized destructor 
}

13.2 copy control and resource management

  • It is crucially important for assignment operators to work correctly, even when an object is assigned to itself. A good way to do so is to copy the right-hand operand before destroying the left-hand operand.
class A{
public:
    int* resource_;
    size_t* count_;

    A(int resource) : resource_(new int(resource)) {
        cout << "In constructor" << endl;
        this->count_ = new size_t(1);
    }

    A(const A& a) {
        cout << "in copy constructor" << endl;
        this->resource_ = a.resource_;
        this->count_ = a.count_;
        *(this->count_) += 1;
        cout << "update count" << *(this->count_) << endl;
    }
    ~A(){
        cout << "In destructor" << endl;
        *(this->count_) -= 1;
        if (*(this->count_) == 0) {
            cout << "count is 0" << endl;
            delete this->resource_;
            delete this->count_;
        }
    }
};

int main() {
    A a1(1);
    A a2(a1);
    {
        A a3(a2);
        cout << *a3.count_ << endl; // 3
    }
    cout << *a2.count_ << endl; // 2
}

In constructor
in copy constructor
update count2
in copy constructor
update count3
3
In destructor
2
In destructor
In destructor
count is 0

13.6 Moving

rvalue references:

Rvalue references refer to objects that are about to be destroyed. Hence, we can “steal” state from an object bound to an rvalue reference.

Lvalues persist; Rvalue Are Ephemeral

variables are lvalue

int i = 42; 
int &r = i; // lvalue reference. correct, r refers to i
int &&rr = i; // rvalue reference, wrong, cannot bind an rvalue reference (rr) to an lvalue (i)

int &&rr1 = 42; // correct, literals are avalue
int &&rr2 = rr1; // error: the expression rr1 is an rvalue

Library move function

int &&rr1 = 42; // correct, literals are avalue
int &&rr2 = rr1; // error: the expression rr1 is an rvalue
int &&rr3 = std::move(rr1); // correct, obtain an rvalue reference bound to an lvalue by move 

Move Constructor and Move Assignment

Move constructors and move assignment operators that cannot throw exceptions should be marked as noexcept.
class A{
public:
    A (A&& s) noexcept {
        this.a_ = s.a_;
        s.a_ = nullptr; // set s.a_ to null to avoid double free
    }

    A& operator=(A&& rhs) noexcept {
        // test for self assignment
        if (this != rhs) {
            free();
            this.a_ = rhs.a_;
            rhs = nullptr; // destory rhs
        }
        return *this; // return reference to this object
    } 
private:
    int* a_;
    void free() {
        if (a_ != nullptr) {
            delete a_;
            a_ = nullptr;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值