c++OOP之复制控制 ------复制构造函数、赋值重载、析构

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

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

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

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

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

一个空类,编译器提供默认无参数构造函数、拷贝构造函数、赋值运算符以及析构函数,一共四个函数。(面试)
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)。
时间: 2024-10-06 13:23:49

c++OOP之复制控制 ------复制构造函数、赋值重载、析构的相关文章

复制控制(下)

消息处理示例 有些类为了做一些工作需要对复制进行控制.为了给出这样的例子,我们将概略定义两个类,这两个类可用于邮件处理应用程序.Message 类和 Folder 类分别表示电子邮件(或其他)消息和消息所出现的目录,一个给定消息可以出现在多个目录中.Message 上有 save 和 remove 操作,用于在指定 Folder 中保存或删除该消息.对每个 Message,我们并不是在每个 Folder 中都存放一个副本,而是使每个 Message 保存一个指针集(set),set 中的指针指向

C++之重载String ----- 构造函数、复制控制、重载操作符

本博文 我们通过 重新实现String类 来说明构造函数,复制控制,重载操作符. 一.构造函数(包括析构函数): 1:默认构造函数: 2:用户自己定义的构造函数 注意:当用户自己定义时,也要明确显示默认构造函数,这是因为,当我们没有定义自己的构造函数时,编译器会为我们自动合成一个,而我们定义了构造函数时,编译器默认构造函数改为我们自己定义的.这时就有可能出现错误: 3:析构函数: 具体声明与实现,代码如下: 1 声明部分: 2 String(); 3 String(const char*s);

C++ Primer 学习笔记_68_面向对象编程 --构造函数和复制控制[续]

面向对象编程 --构造函数和复制控制[续] 三.复制控制和继承 合成操作对对象的基类部分连同派生类部分的成员一起进行复制.赋值或撤销,使用基类的复制构造函数.赋值操作符或析构函数对基类部分进行复制.赋值或撤销. 类是否需要定义复制控制成员完全取决于类自身的直接成员.基类可以定义自己的复制控制而派生类使用合成版本,反之,基类使用合成版本,而派生类使用自己定义的复制控制也可以. 只包含类类型或内置类型的数据成员.不包含指针的类一般可以使用合成操作,复制.赋值或撤销这样的成员不需要使用特殊控制.但是:

C++ Primer 学习笔记_68_面向对象编程 -构造函数跟复制控制[续]

面向对象编程 --构造函数和复制控制[续] 三.复制控制和继承 合成操作对对象的基类部分连同派生类部分的成员一起进行复制.赋值或撤销,使用基类的复制构造函数.赋值操作符或析构函数对基类部分进行复制.赋值或撤销. 类是否需要定义复制控制成员完全取决于类自身的直接成员.基类可以定义自己的复制控制而派生类使用合成版本,反之,基类使用合成版本,而派生类使用自己定义的复制控制也可以. 只包含类类型或内置类型的数据成员.不包含指针的类一般可以使用合成操作,复制.赋值或撤销这样的成员不需要使用特殊控制.但是:

15.4——构造函数与复制控制

构造函数与继承: (1)构造函数和复制控制成员不能继承,每个类定义自己的,要是没定义就用合成的. 对于基类来说: (1)基本没啥影响,若是只希望派生类来使用,可以定义为protected. 对于派生类来说: (1)派生类构造函数除了初始化自己的成员外,还要初始化基类(这个先进行). (2)若是合成的派生类默认构造函数,它将先调用基类的合成默认构造函数,再接着根据常规变量初始化自己的. (3)定义默认构造函数时的初始化列表可以只写派生类的,会自动隐含的调用基类的. (4)不能直接初始化继承类的成员

c++ 复制控制之复制构造函数

什么是复制构造函数? 只有单个形参, 而且该形参是对本类类型对象的引用( 常用const修饰), 这样的构造函数称为复制构造函数. 什么时候使用复制构造函数? 1. 根据另一个同类型的对象显示或隐式初始化一个对象. 2. 复制一个对象, 将它作为实参传给一个函数. 3. 从函数返回时复制一个对象. 4.初始化顺序容器中的元素. 5.根据元素初始化列表初始化数组元素.  1)对象的定义形式      c++支持两种初始化形式:  直接初始化和复制初始化. 复制初始化使用=符号, 而直接初始化将初始

第十九篇:复制控制( 下 ) --- 自定义析构函数

前言 经过前两篇随笔( 上  中 )的分析我们已经解决了具有指针成员的同类对象“ 干涉 ”问题.可惜,前面给出的解决方案代码还是不完整.还有什么问题呢?观察发现,构造函数里面有new的关键字出现,也就是说开辟了新的内存空间,我们也知道new必须也只能对应一个delete,而不应该让系统自己处理( 还不熟练new和delete用法的点这里 ),但这里和new对应的delete去哪里了? 解决思路 应该何时delete?显然,应该在对象销毁的时候delete.C++中定义了这样一种函数 --- 析构

稍微深入点理解C++复制控制【转】

通过一个实例稍微深入理解C++复制控制过程,参考资料<C++ primer>,介绍点基本知识: 1.在C++中类通过特殊的成员函数:复制构造函数.赋值操作符和析构函数来控制复制.赋值和撤销该类的对象时会发生什么. 2.复制构造函数(copy constructor)是一种特殊的构造函数,具有单个形参,该形参(常用const)是对该类类型的引用: 当定义一个新对象并用一个同类型的对象对它进行初始化时,将显示使用复制构造函数: 当将该类的对象传递给函数或从函数返回该类型的对象时,将隐式使用复制构造

C++拾遗(六)——复制控制

年前忙了几天,到现在才算是有空休息下来.先祝大家新年快乐,心想事成:)我也会发笑脸o.o 这篇博文主要介绍定义一个类型的对象时的复制控制方式,这部分内容之前有一定的了解但又浅尝辄止,始终感觉没能找到要点.年前又拿起书细细品读,算是有了一点新的了解.几天前就想动笔了,一直没时间,拖到现在. 每种类型定义了创建该类型的对象时会发生什么——构造函数定义了该类类型对象的初始化.类型还能控制复制.赋值或撤销该类型的对象时会发生什么——类通过特殊的成员函数:复制构造函数.赋值操作符和析构函数来控制这些行为.