首页 > 代码库 > 0717-----C++Primer听课笔记----------复制控制

0717-----C++Primer听课笔记----------复制控制

1.默认拷贝构造函数

1.1 编译器自动为我们合成一个拷贝构造函数。A(const A &).

1.2 象复制的时机

a) 显式复制。

b) 使用对象做形参

c) 使用对象做返回值

d) 往容器中放入对象

#include <iostream>#include <string>#include <vector>using namespace std;/* *用一个对象初始化另一个对象时 需要调用拷贝构造函数 *这里 编译器 自动提供了一个默认的拷贝构造函数 */class Student{    public:        Student();        void setStudent(const string &name, int age, int score);        void print() const;    private:        string name_;        int age_;        int score_;};Student::Student()    :name_(""),     age_(0),     score_(0){}void Student::setStudent(const string &name, int age, int score){    name_ = name;    age_ = age;    score_ = score;}void Student::print()const{    cout << name_ << " " << age_ << " " << score_ << endl;}int main(int argc, const char *argv[]){    Student s;    s.setStudent("monica", 22, 99);    Student s2(s); //此时需要调用拷贝构造函数    s2.print();    Student s3("copy", 33, 89);    return 0;}

2.深拷贝和浅拷贝

2.1 概念:浅拷贝和深拷贝是对类中持有指针而言,如果对象复制的时候,仅仅去拷贝指针的值,这称为浅拷贝(shallow copy),如果不是去拷贝指针的值,而是去拷贝指针指向的内存空间,称为深拷贝(deep copy)。

2.2 浅拷贝的例子 ,自己实现一个 string类,并用一个对象去初始化另一个对象,这里没有写拷贝构造函数,因此编译器会自动生成默认的拷贝构造函数,但是请注意,这里的string 成员变量是用指针实现的,默认的拷贝构造函数只复制了指针,因此两个对象将指向同一个内存区域,这样,在调用析构函数的时候,同一片内存就会delete两次,这就造成了错误。

#include <iostream>#include <string.h>using namespace std;/* * 自定义string类 调用默认构造函数时, * 仅仅复制了str的值,复制后,两个string对象 * 指向同一块内存,在析构的时候 该内存被delete * 两次 因此出错 */class  String{    public:        String();        String(const char* str);        ~String();        void debug() const;    private:        char *str_;};String::String()    :str_ (new char[1]){     str_[0] = 0;}String::String(const char* str) //用C 字符串初始化一个string 对象    :str_(new char[strlen(str) + 1]);{    strcpy(str_, str);}String::~String(){    delete[] str_;}void String::debug()const{    cout << str_ << endl;}int main(int argc, const char *argv[]){    String s1("apple");    s1.debug();    String s2(s1);    s2.debug();    return 0;}

图像 2

2.3 深拷贝的例子,还是上例,定义自己的拷贝构造函数,在初始化另一个对象时,为对象分配新的内存空间,并且将值拷贝过去。这里在执行完深拷贝之后,两个对象之间没有任何关联。

#include <iostream>#include <string.h>using namespace std;/* * 深拷贝 */class  String{    public:        String();        String(const char* str);        String(const String &other); //自定义 拷贝构造函数        ~String();        void debug() const;    private:        char *str_;};String::String()    :str_ (new char[1]){     str_[0] = 0;}String::String(const char* str) //用C 字符串初始化一个string 对象    :str_(new char[strlen(str) + 1]){    strcpy(str_, str);}String::String(const String &other)    :str_(new char[strlen(other.str_) + 1]){    strcpy(str_, other.str_);}String::~String(){    delete[] str_;}void String::debug()const{    cout << str_ << endl;}int main(int argc, const char *argv[]){    String s1("apple");    s1.debug();    String s2(s1);    s2.debug();    return 0;}
3. 赋值运算
3.1 对象的赋值,调用的是类的赋值运算符。编译器自动为我们合成一个赋值运算符。这里我为string类编写一个复制运算符函数,但是没有考虑自身赋值的问题。

#include <iostream>#include <string.h>using namespace std;/* * 编译器会自动为我们合成一个赋值运算符 * 也可以自定义一个 赋值运算符 */class  String{    public:        String();        String(const char* str);        String(const String &other);        String &operator= (const String &other); // 自定义赋值运算符        ~String();        void debug() const;    private:        char *str_;};String::String()    :str_ (new char[1]){     str_[0] = 0;}String::String(const char* str)    :str_(new char[strlen(str) + 1]){    strcpy(str_, str);}String::String(const String &other)    :str_(new char[strlen(other.str_) + 1]){    cout << " call copy construction " << endl;    strcpy(str_, other.str_);}String& String::operator=(const String &other){    cout << "call operator= func " << endl;    delete[] str_;    str_ = new char[strlen(other.str_) + 1];    strcpy(str_, other.str_);    return *this;}String::~String(){    delete[] str_;}void String::debug()const{    cout << str_ << endl;}int main(int argc, const char *argv[]){    String s1("apple"); //调用第二个构造函数    s1.debug();    String s2(s1); // 调用拷贝构造函数    s2.debug();    String s3 ;//调用自定义的赋值运算符    s3 = s1;    s3.debug();    return 0;}

3.2 编写赋值运算符和拷贝构造函数的区别:

a) 赋值运算符可能需要先释放资源

b) 赋值预算符需要考虑自身赋值问题。

#include <iostream>#include <string.h>using namespace std;/* * 编写赋值运算符 必须考虑 自身赋值的问题 * 并且 返回值必须是自身的引用 */class  String{    public:        String();        String(const char* str);        String(const String &other);        String &operator= (const String &other); // 自定义赋值运算符        ~String();        void debug() const;    private:        char *str_;};String::String()    :str_ (new char[1]){     str_[0] = 0;}String::String(const char* str)    :str_(new char[strlen(str) + 1]){    strcpy(str_, str);}String::String(const String &other)    :str_(new char[strlen(other.str_) + 1]){    cout << " call copy construction " << endl;    strcpy(str_, other.str_);}String& String::operator=(const String &other){    cout << "call operator= func " << endl;    if(&other != this){ //这里比较用的是指针 而不是那对象本身直接去比较        delete[] str_;        str_ = new char[strlen(other.str_) + 1];        strcpy(str_, other.str_);    }    return *this;}String::~String(){    delete[] str_;}void String::debug()const{    cout << str_ << endl;}int main(int argc, const char *argv[]){    String s1("apple"); //调用第二个构造函数    s1.debug();    String s3 ;//调用自定义的赋值运算符    s3 = s1;    s3.debug();    s3 = s3;    s3.debug();    return 0;}

3.3 总结

3.3.1 编写赋值预算符的准则:

a) 必须处理自身赋值问题;

b) 必须返回自身引用。

3.3.2 三法则:拷贝构造函数、赋值运算符、析构函数,三者都与类内部的指针密切相关。当需要编写其中一个时,一般需要编写其他两个。

3.3.3 当需要禁止一个类进行复制或者赋值时,只需将类的拷贝构造函数和赋值运算符设为私有,而且只提供声明(注意设为私有后 该类的友元还可以访问,因此要只提供声明,这样友元类在要调用这两个函数时,会在链接时导致错误)。根据谷歌编程规范(http://zh-google-styleguide.readthedocs.org/en/latest/google-cpp-styleguide/contents/),可以将其定义为宏。

#include <iostream>#include <string>#include <vector>using namespace std;/* * 禁止 拷贝和赋值 * 将对函数设为私有 */class Student{    public:        Student();        void setStudent(const string &name, int age, int score);        void print() const;    private:        Student(const Student &other);        Student &operator= (const Student &other);        string name_;        int age_;        int score_;};Student::Student()    :name_(""),     age_(0),     score_(0){}void Student::setStudent(const string &name, int age, int score){    name_ = name;    age_ = age;    score_ = score;}void Student::print()const{    cout << name_ << " " << age_ << " " << score_ << endl;}int main(int argc, const char *argv[]){    Student s;    s.setStudent("monica", 22, 99);    Student s2(s);    s2.print();    return 0;}

该宏定义如下:

#define DISALLOW_COPY_AND_ASSIGN(TypeName)\     TypeName(const TypeName&);     void operator=(const TypeName&)