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;
}

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

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&)

0717-----C++Primer听课笔记----------复制控制,布布扣,bubuko.com

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

上一篇:javascript瀑布流效果


下一篇:杂谈X509证书, Java Keystore与Jetty