IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++特殊成员函数(深拷贝和深赋值) -> 正文阅读

[C++知识库]C++特殊成员函数(深拷贝和深赋值)

码文不易,觉得内容不错点个关注吧~

在C++的类中有这么一些成员函数是自动定义的,具体如下所示:

1.默认构造函数

2.默认析构函数

3.复制构造函数(即拷贝构造函数)

4.赋值运算符

5.地址运算符

C++11提供了另外的两个特殊成员函数:移动构造函数和移动赋值运算符,在后讨论。

本文仅讨论1,3,4的问题

一.默认构造函数

在不定义构造函数时,会自动定义一个默认构造函数。反之则不会定义。如果想在创建对象的时候不显式地初始化,就必须自己显式地定义默认构造函数。这种构造函数没有参数,有两种方式:1.不带任何参数的默认构造。2.带default值的有参构造函数。

注意:这两种定义不能同时存在,不然的话在用默认构造创建对象的时候会有二义性。

Klunk(int n = 0){klunk_ct=n};//定义1
Klunk(){klunk_ct=0};//定义2

二.复制构造函数(拷贝构造函数)

2.1何为拷贝构造函数,何时调用

拷贝构造函数用于将一个对象复制到新建对象中。一言以蔽之,初始化过程中。

常见原型 Class_name(const Class_name &)

它接受一个指向类对象的常量作为参数,以下为调用拷贝构造函数的4种声明

Stringbad copy_1(initial)// Stringbad是类名,copy_n为新对象名,initial为旧对象名
Stringbad copy_2 = initial;
Stringbad copy_3 = Stringbad(initial);

Stringbad * pcopy_3 = new Stringbad(initial);

2.2默认拷贝构造函数的功能与缺陷

默认拷贝构造函数逐个复制非静态成员,复制的是成员的值。(这就是浅拷贝)

注意:静态成员不受影响,因为它们属于整个类,所有的对象都共享同一份,因此不会被操作。

为了说明浅拷贝的危害,下面举一个例子来说明。这是一个有缺陷的Stirng类的实现,因此将他命名为Stringbad。

class Stringbad {
private:
    char *str; //声明一个char*而不具体的定义多大空间,这样能够根据需要来,而不会浪费。
    int len;// 字符串的长度
    static int num_strings;//一个静态成员,注意只有在这里声明才需要static,待会使用的时候是没有static的
public:
    Stringbad(const char *a);//带参构造
    Stringbad();//默认构造
    ~Stringbad();//析构函数
    friend std::ostream & operator<<(std::ostream &os, const Stringbad &st);//<<运算符重载,为了能以<<对象的方式就打印。
};

Stringbad::Stringbad(const char *a) {
    len =std::strlen(a);
    str = new char[len+1];//because it ignore the last null char.
    std::strcpy(str,a);
    num_strings++;
    cout<<num_strings<<": "<<str<<"  "<<"object created\n";
}

Stringbad::Stringbad() {
    len = 4;
    str = new char[len];
    std::strcpy(str,"C++");
    num_strings++;
    cout<<num_strings<<": "<<str<<"  "<<"object created\n";
}

Stringbad::~Stringbad() {
    cout<<num_strings<<": "<<str<<"  "<<"object delted\n";
    --num_strings;
    cout<<"Now has "<<num_strings<<"  left\n";
    delete []str;
}

std::ostream & operator<<(std::ostream &os,const Stringbad &st)
{
    os<<st.str;
    return os;
}

接着我们在main函数中调用以下这个测试:

void test01(){
    Stringbad a1 = "ddd";
    Stringbad a2 = "mmm";
    Stringbad copy_1 = a1;
    cout<<copy_1;
}

?1.我们先通过构造函数创建了两个对象a1,a2,在构造函数中我们每次都会让num_string这个值+1,因此为2,说明有两个对象了。

2.然后我们使用默认拷贝构造将a1拷贝给了copy_1,但是默认拷贝构造只是简单的将成员的值复制了一下而已,并没有使静态成员发生变化,因此我们的num_string还是2.(缺陷1)

3.由于对象的创建顺序是a1,a2,copy_1因此析构的顺序也应该是copy_1,a2,a1。我们观禅控制台的输出,1.发现先是ddd被delete了,这个是copy_1的成员变量str被delete了。2.接着是mmm,也正常地被析构了3.然后是a1的mmm,但是并没有正常地打印出内容这是为什么呢??因为浅拷贝只是简单的赋值而已,它将指向a1的str的指针直接拿给了copy_1,相当于这两个对象都指向了同一片区域,因此对copy的str删除,将使a1指向的str也没有了ps:就是同一个(缺陷2)

总结:1.不能拷贝静态变量

?????????? 2.在存在指针的情况下,并没有给新对象独立的区域而是简单的赋值。

3.3解决方案:深拷贝 ?????????

深拷贝就是在上述的缺陷中提出来的,代码实现如下。非常简单,就是将C++没有给我们考虑好的拷贝要素自己写好就OK了,这就是深拷贝,这样就是真正独立的一个对象了,并不依靠于别的对象。

Stringbad::Stringbad(const Stringbad &t) {
    num_strings++;
    len = t.len;
    str = new char [len+1];
    std::strcpy(str,t.str);
    cout<<num_strings<<": "<<str<<"  "<<"object created\n";
}

?三.赋值运算符

3.1赋值运算符的来由,我们为何能隐式构造

现在我们来关注一下这个写法Stringbad copy_2 = initial;

显然这就是拷贝构造嘛,前面讲的隐式构造。但是!为什么这样是可以的呢?C语言没有类,当然不会支持它,C++为什么支持呢?其实这里是C++通过为我们类重载赋值运算符实现的,这种运算符的原型如下:

??????? Class_name & Class_name::operator=(const Class_name &);

那你可能会奇怪了,那我们直接重写一个赋值运算符就好了,不用写拷贝构造了,因为像Stringbad copy_2 = intial这样的写法,能够直接被匹配。

实际上初始化对象时并不一定会使用赋值运算符。

上述语句实现的时候可能会分两布处理该语句:1.使用拷贝构造函数创建一个临时变量2.通过=赋值重载复制到新对象中。

也就是说初始化总会调用靠背构造,而使用=运算符也允许调用赋值运算符。

3.2赋值的错误

赋值的错误如下所示,a1用a2赋值了,从而先析构a2的时候str内容已经消失了,再析构a1的时候以及该地质以及没有内容了,会出现数据受损的危害。

void test01(){
    Stringbad a1 = "ddd";
    Stringbad a2 = "mmm";
    a1 = a2;
    cout<<a1;
}

3.3解决赋值问题(深赋值)

与深拷贝一样,深赋值就是把浅操作的问题都弥补一下,但是是有所差别的需要注意如下:

1.由于目标对象可能引用了以前分配的数据,所以需要使用delete[]来释放这些数据防止内存泄漏。

2.函数应该避免赋给自己;否则在给对象重新赋值前会将该对象的内容删除

3.函数返回一个指向调用对象类型的引用

Stringbad &Stringbad::operator=(const Stringbad &st) {
    if(this == &st)
    {
        return *this;
    }
    delete []str;
    len = st.len;
    str = new char[len+1];
    std::strcpy(str,st.str);
    return *this;
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 18:05:26  更:2022-03-30 18:08:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:28:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码