zoukankan      html  css  js  c++  java
  • c++OOP之复制控制 ------复制构造函数、赋值重载、析构

    本博文我们讨论OOP复制控制的一些内容;

    首先考虑对象复制的时机: 非引用类型

    1):根据一个类去显式或者隐式初始化一个对象;

    2):复制一个对象,将它作为实参传给一个函数;

    3):从函数返回时复制一个对象。( string    tolittle(string word))

    4):初始化顺序容器中的元素。(例如vector 必须具备 copy,assignment功能)

    一个空类,编译器提供默认无参数构造函数、拷贝构造函数、赋值运算符以及析构函数,一共四个函数。(面试)!!
    
    11.复制构造函数、赋值运算符以及析构函数,称为三法则,一旦提供了其中一个,务必提供其余两个。以String为例:
    a)  涉及到深拷贝、浅拷贝问题,所以需要提供拷贝构造函数
    b)  然后,为了保持一致,赋值运算符也应该实现深拷贝
    c)  既然实现深拷贝,那么必定申请了资源(例如内存),所以必然需要析构函数来手工释放。
    

     一:复制构造函数:

    复制构造函数调用的时机就是在对象复制的时候。方式有两种:

    1):当用户不提供时,编译器自动为我们合成一个拷贝构造函数;

    示例代码如下:

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Student 
    {
        public:
            Student()
            {}
            Student(int id, const string&name, int age)
                :id_(id),name_(name),age_(age)
            {}
            
            void print()
            {  cout << id_ <<":" << name_ <<":" << age_ << endl; }
        private:
            int id_;
            string name_;
            int age_;
    };
    
    int main(int argc, const char *argv[])
    {
        Student s(11,"zhangsan",23);
        s.print();
    
        Student s2(s); //执行此句,调用默认复制构造函数
        s2.print();
        return 0;
    }
    

     2):用户自己定义:

    放入class Student 的public中即可;

    1   Student(const Student &s)
    2         {
    3             id_ = s.id_;
    4             name_ = s.name_;
    5             age_ = s.age_;
    6         }

    复制构造函数之 深copy 和 浅copy;

    含有指针成员变量的类在复制时,有两种选择:
    a) 复制指针的值,这样复制完毕后,两个对象指向同一块资源,这叫做 浅拷贝shallow copy(有可能出错)
    b) 复制指针所指向的资源,复制完毕后,两个对象各自拥有自己的资源,这叫做 深拷贝 deep copy
    

     示例代码:

     1 #include "iostream"
     2 #include "string.h"
     3 using namespace std;
     4 
     5 //copy String 时,仅仅复制指针的值
     6 //导致析构时发生了问题
     7 class String
     8 {
     9     public:
    10         String();
    11         String(const char*s);
    12         String(const String &s);
    13         ~String();       
    14         void print()const
    15         {
    16             cout << str_ << endl;
    17         }
    18     private:
    19         char *str_;
    20 };
    21 String::String()
    22     :str_(new char(0))
    23 {  }
    24 String::String(const char *s)
    25     :str_(new char[strlen(s)] +1)
    26 {
    27     ::strcpy(str_, s);
    28 }
    29 //程序结束时会调用析构函数,两个对象s1,s2必然会析构两次,而str_指向同一片区域;
    30 //当析构s1时,s1.str_所指向的区域被释放;而s2.str_ 也同时被释放
    31 //当析构s2时,由于s2.str_已经被s1.str_释放,所以会产生错误.
    32 String::String(const String &s)
    33     :str_(s.str_)
    34 {}
    35 
    36 /* 修改成以下即可:
    37  *String::String(const String &s)
    38  *     :str_(new char[strlen(s)] +1)
    39  * {
    40  *  ::strcpy(str_, s.str_);
    41  *  }
    42  */
    43 String::~String()
    44 {
    45     delete[] str_; //注意
    46 }
    47 int main(int argc, const char *argv[])
    48 {
    49     String s("helloworld");
    50     s.print();
    51     
    52     String s2(s);
    53     s.print();
    54     return 0;
    55 }

    二: 赋值运算符的重载:【因为我们自定义的类没有内置比较运算符(<、>、!=)】
    示例代码如下:

     1 #include <iostream>
     2 #include <string.h>
     3 using namespace std;
     4 
     5 class String
     6 {
     7     public:
     8         String();
     9         String(const char *s);
    10         String(const String &s);
    11         ~String();
    12         String &operator = (const String &s);
    13         size_t size()const //自己构造size函数
    14         {
    15             return strlen(str_);
    16         }
    17         
    18         void print()
    19         { cout << str_ << endl; }
    20     private:
    21        char *str_;
    22 };
    23 String::String()
    24     :str_(new char[1])
    25 { *str_ = 0; }
    26 
    27 String::String(const char*s)
    28     :str_(new char(strlen(s)+1))
    29 { ::strcpy(str_, s); }
    30 
    31 String::String(const String &s)
    32     :str_(new char(strlen(s.str_)+1))
    33 { ::strcpy(str_, s.str_); }
    34 
    35 String &String::operator = (const String &s)
    36 {
    37    if(this == &s)//自身赋值
    38         return *this;
    39     delete[]str_;
    40     str_ = new char(s.size() + 1 );
    41     ::strcpy(str_, s.str_);
    42     
    43     return *this;
    44 }
    45 
    46 String::~String()
    47 { delete[] str_; }
    48 
    49 int main(int argc, const char *argv[])
    50 {
    51     String s("helle");
    52     s.print();
    53     
    54     String s2;
    55     s2 = s; //赋值重载
    56     s2.print();
    57     
    58     s2 = s2 ; //注意自身赋值情形
    59     s2.print();
    60     
    61     return 0;
    62 }

    禁止类复制和赋值的方法:

    a):把 copy构造函数和复制运算符设为private;

    b):只声明,不实现;

     1 #include <iostream>
     2 #include <string>
     3 #include <vector>
     4 using namespace std;
     5 
     6 //no copy,no assignment
     7 //google 推荐写法
     8 #define   DISALLOW_COPY_AND_ASSIGN(TypeName) 
     9             TypeName(const TypeName&); 
    10                 void operator=(const TypeName&)
    11 
    12 class Test
    13 {
    14     public:
    15         Test(){}
    16         ~Test(){}
    17 
    18     private:
    19         Test(const Test&t);
    20         void operator=(const Test &t);
    21 };
    22 
    23 int main(int argc, const char *argv[])
    24 {
    25     Test t;
    26     
    27     Test t2(t);//error
    28 
    29     Test t3;
    30     t3 = t; //error
    31     
    32     return 0;
    33 }
    a):如果一个类,不需要复制和赋值,那就禁用这种能力,这可以帮助避免大量潜在的bug。
    b):如果一个类,实现了像value一样的复制和赋值能力(意味着复制和赋值后,
    两个对象没有任何关联,或者逻辑上看起来无任何关联),那么就称这个类的对象为  值语义(value semantics)。
    如果类不能复制,或者复制后对象之间的资源归属纠缠不清,那么称为 对象语义(object semantics),或者 引用语义(reference semantics)。
    
  • 相关阅读:
    CentOS7安装iptables防火墙
    Linux下ntpdate时间同步
    linux下的时间及时区设置
    linux下的DNS
    sysctl
    ab -n -c
    VIM 中 查看{}是否闭合,按%跳转到下个闭合
    要删除共享的初始登陆名 cmd下输入net use * /delete
    case in esac ` for in do done ` while true / false
    read op case $op in
  • 原文地址:https://www.cnblogs.com/xfxu/p/3994038.html
Copyright © 2011-2022 走看看