zoukankan      html  css  js  c++  java
  • C++ 复制构造函数 与 赋值运算符

    在C++中,将一个对象赋给另外一个对象,编译器将提供赋值运算符的定义。

    有两种情况,下面假设catman是Monster的一个实例

    第一种:初始化

    Monster golblen= catman;

    第二种:普通赋值

    Monster golblen;

    golblen= catman;

    复制构造函数

    其中,第一种情况,系统将调用复制构造函数,其原型为

    Monster(const Monster& monster);

    如果Monster里没有提供该函数,编译器将自动提供,编译器自动提供时

    Monster monster3 = monster1;

    相当于

    Monster monster3;
    monster3. name = monster1.name

    由于monster3的name实际上共享monster1的name,所以这种复制叫做“浅复制”,两个对象共享同一份成员变量,在析构时会报错。

    解决错误的方式是自己定义一个复制构造函数,并且自己为name开辟空间,将monster1的name使用strcpy_s的方式拷贝到自己开辟的空间里

    顺带一提,monster2是用new创建的,new出来的对象存活于堆中,其他两个对象存活于栈中,系统只会自动释放栈内空间,而堆内空间需要用户自己维护。

    Monster类声明

    #pragma once
    #include <iostream>;
    
    using std::ostream;
    using std::cin;
    using std::cout;
    using std::endl;
    
    class Monster {
    
    private:
        char* name;
    
    public:
        Monster();
        Monster(const char* name);
        Monster(const Monster& monster);
        ~Monster();
        friend ostream& operator<<(ostream& os, const Monster& monster);
    };

    Monster类定义

    #include "Monster.h";
    
    Monster::Monster()
    {
        cout << "默认构造函数执行完毕" << endl;
    }
    
    Monster::Monster(const char* name)
    {
        this->name = new char[strlen(name) + 1];
        strcpy_s(this->name, strlen(name) + 1, name);
        cout << "构造函数执行完毕" << endl;
    }
    
    //复制构造函数,如果没有定义,编译器将自动提供浅复制方式的复制构造函数
    Monster::Monster(const Monster& monster)
    {
        this->name = new char[strlen(monster.name) + 1];
        strcpy_s(this->name, strlen(monster.name) + 1, monster.name);
        cout << "复制构造函数执行完毕" << endl;
    }
    
    Monster::~Monster()
    {
        cout << name << "has been destroyed." << endl;
        delete[] name;
    }
    ostream& operator<<(ostream& os, const Monster& monster)
    {
        return os << monster.name;
    }
    
    int main(void)
    {
        {
            Monster monster1("Golblen");
            cout << "monster1: " << monster1 << " online" << endl;
                
            //下面这种初始化方式,将会调用复制构造函数
            Monster monster2 = monster1;
            cout << "monster2: " << monster2 << " online" << endl;
    
            Monster monster3;
            monster3 = monster1;
            cout << "monster3: " << monster2 << " online" << endl;
        }
        cin.get();
        return 0;
    }

    运行结果

    报错了,看来赋值的时候,并没有调用自定义的复制构造函数,所以释放name时出错了

    赋值运算符

    解决上面报错的问题,自需要增加一个成员函数,

    函数原型

    Monster& operator=(const Monster& monstere);

    函数定义

    Monster& Monster::operator=(const Monster& monster)
    {
        if (this == &monster)
        {
            return *this;
        }
        delete[] this->name;
        int length = strlen(monster.name);
        name = new char[length+1];
        strcpy_s(name, length + 1, monster.name);
        cout << "赋值运算符调用完毕" << endl;
        return *this;
    }

    定义赋值运算符时,必须要做的三件事情:

    1.由于目标对象已经引用了之前分配的数据,所以一定要使用delete[]来释放这些数据,否则将出现内存泄漏

    2.函数应当避免将对象赋给自身,否则做第1步操作释放数据时,会将对象的数据也释放掉

    3.函数应当返回一个指向当前对象的调用

    另外,赋值运算符只能由类成员函数重载,为什么呢 (见https://bbs.csdn.net/topics/392049284?page=1 )

    1:对于赋值操作符(=)--比较特别,因为任何类如果不提供显示的拷贝赋值(即重载=),则编译器会隐式地提供一个。这样的话,如果你再通过友元声明,进行全局的定义会造成调用二义性(即使允许,编译也会出错)

    2:对于所有楼主提到的操作符(=,[],(),->),只能声明为成员函数是为了避免不合法的书写通过编译(这是推测出的原因,更深层的可能要研究 C++ 的设计了)

  • 相关阅读:
    crontab自动备份MySQL数据库并删除5天前备份
    使用ShowDoc在线管理API接口文档
    概率计算(抽奖活动、命中率)
    保护隐私?找回已记住的秘密?你的余额宝、淘宝还安全吗?
    自制公众平台Web Api(微信)
    我为什么期待M#?
    在.net中为什么第一次执行会慢?
    记”Uri.IsWellFormedUriString”中的BUG
    公司ERP系统重构那些事
    Koala Framework是什么?我为什么要写这个框架?
  • 原文地址:https://www.cnblogs.com/heben/p/9411035.html
Copyright © 2011-2022 走看看