在C++中,经常会将对象进行赋值或拷贝,在C++中,在程序员没有手动编写相关内容时,系统会自动创建拷贝构造函数和复制操作符内容,对于值类型数据使用系统默认的函数可以正常运行,但是对于类类型、指针类型,系统自动自动创建的操作函数无法将类类型、指针类型创建副本,而是两个类对象同时持有一个拷贝副本,改变一个类对象数据,会造成另个一类对象数据改变,造成隐性bug。
在编写拷贝构造函数和赋值操作符时注意事项:
数据类:
.h
#ifndef DATA_CLASS_H
#define DATA_CLASS_H
#include
/**
* @copyright 2008-2023
* @date 2023-07-14
* @author qiaowei
* @version 1.0
* @brief 数据类。有2个变量,分别是QString,int类型。
* @history None
* @function None
* @field None
*/
class DataClass : public QObject
{
Q_OBJECT
public:
explicit DataClass(QObject *parent = nullptr);
/**
* @author qiao wei
* @version 1.0
* @brief Copy constructor.
* @param value 拷贝的变量。
* @return None
*/
DataClass(DataClass& value);
virtual ~DataClass();
DataClass& operator =(const DataClass& value);
public:
QString name_;
int level_;
};
#endif // DATA_CLASS_H
.cpp
#include
#include "data-class.h"
DataClass::DataClass(QObject *parent)
: QObject{parent}
, name_{""}
, level_{0}
{}
DataClass::DataClass(DataClass& value)
{
// QObject's copy constructor and assignment operator are declared in a private section.
if ((nullptr != &value) && (this != &value)) {
this->name_ = value.name_;
this->level_ = value.level_;
}
}
DataClass::~DataClass()
{}
DataClass &DataClass::operator =(const DataClass &value)
{
// QObject's copy constructor and assignment operator are declared in a private section.
if (this == &value) {
return *this;
}
// 赋值操作符要对指针字段先进行delete,使用nullprt赋值。保证不会出现内存泄漏。
this->name_ = value.name_;
this->level_ = value.level_;
return *this;
}
类中的两个字段分别为QString、int类型。作为基础类型,也可以不用重写拷贝构造函数和赋值操作符。
父类BaseClass有2个字段,分别是DataClass指针data_,QString类型local_,因为字段data_为类指针,所有BaseClass必须重写拷贝构造函数和赋值操作符。
注意事项:
.h文件:
#ifndef BASECLASS_H
#define BASECLASS_H
#include
#include "data-class.h"
/**
* @copyright 2008-2023
* @date 2023-07-07
* @author qiaowei
* @version 1.0
* @brief None
* @history None
* @function None
* @field None
*/
class BaseClass : public QObject
{
Q_OBJECT
public:
/**
* @author qiao wei
* @contact [email protected]
* @version 1.0
* @other Used/Unused
* @brief Constructor。构造方法不能调用virtual方法。
* @param None
* @return None
*/
explicit BaseClass(QObject *parent = nullptr);
/**
* @author qiao wei
* @contact [email protected]
* @version 1.0
* @other Used/Unused
* @brief Copy constructor。需要调用拷贝构造方法时不会调用其它的构造方法。同构造方法不能调用
* virtual方法。
* @param value 要拷贝的变量。
* @return None
*/
BaseClass(const BaseClass& value);
/**
* @author qiao wei
* @contact [email protected]
* @version 1.0
* @other Used/Unused
* @brief Destructor.
* @param None
* @return None
*/
virtual ~BaseClass();
/**
* @author qiao wei
* @contact [email protected]
* @version 1.0
* @other Used/Unused
* @brief Assignment Operator.
* @param value 赋值的变量。
* @return None
*/
BaseClass& operator =(const BaseClass& value);
public:
/**
* @Date 2023-07-07
* @Author qiaowei
* @Contact [email protected]
* @Version 1.0
* @Brief 指针字段,类类型数据。
*/
DataClass* data_;
/**
* @Date 2023-07-07
* @Author qiaowei
* @Contact [email protected]
* @Version 1.0
* @Brief 字段。
*/
QString local_;
};
#endif // BASECLASS_H
.cpp文件:
#include
#include "base-class.h"
BaseClass::BaseClass(QObject* parent)
: QObject{parent}
, data_{new DataClass{}}
, local_{"local address"}
{}
BaseClass::BaseClass(const BaseClass& value)
{
if ((nullptr != &value) && (this != &value)){
/**
* 注意事项:
* 调用父类拷贝构造函数,因为QObjet的构造函数为private,故不可调用。
* 拷贝构造函数是构造函数的一种,所有字段都是首次调用初始化,不需要对指针字段进行判断,进行delete避免内存泄漏
* 情况。
*/
// 直接对指针字段进行new。
data_ = new DataClass(*(value.data_));
local_ = value.local_;
}
}
BaseClass::~BaseClass()
{
// local_字段是基本类型,无需析构。在析构方法中只对指针字段进行处理,避免memory leak。
if (nullptr != data_) {
delete data_;
data_ = nullptr;
}
}
BaseClass &BaseClass::operator =(const BaseClass& value)
{
// value就是当前字段本身,无需赋值,直接返回。
if (this == &value) {
return *this;
}
// Avoid memory leak.
if (nullptr != data_) {
delete data_;
data_ = nullptr;
}
// 按照字段在类中的声明顺序,依次赋值。
data_ = new DataClass(*(value.data_));
local_ = value.local_;
return *this;
}
运行测试:
#include
#include
#include "deprived-test/base_class.h"
#include "deprived-test/data_class.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
BaseClass* base = new BaseClass(nullptr);
qDebug()<< base->data_->name_ << endl;
qDebug()<< base->data_->level_ << endl;
BaseClass base01(*base);
base01.data_->name_ = "test";
qDebug()<< base->data_->name_ << endl;
qDebug()<< base01.data_->name_ <
测试结果:
""
0
""
"test"
可以看到base01由base赋值,随后对base01的字段进行了修改,打印两者的字段,发现base的字段没有跟随base01的值而改变。所以指针字段分离。
DeprivedClass是BaseClass的子类,在拷贝构造方法,赋值操作符中对父类的相关操作进行调用。
.h
#ifndef DEPRIVEDCLASS_H
#define DEPRIVEDCLASS_H
#include
#include "base-class.h"
class DeprivedClass : public BaseClass
{
Q_OBJECT
public:
explicit DeprivedClass(QObject *parent = nullptr);
/**
* @author qiao wei
* @contact [email protected]
* @version 1.0
* @other Used/Unused
* @brief Copy constructor.
* @param value DeprivedClass reference.
* @return None
*/
DeprivedClass(const DeprivedClass& value);
virtual ~DeprivedClass();
DeprivedClass& operator =(const DeprivedClass& value);
public:
DataClass* deprived_data_;
};
#endif // DEPRIVEDCLASS_H
.cpp
#include "deprived-class.h"
DeprivedClass::DeprivedClass(QObject *parent)
: BaseClass{parent}
, deprived_data_{new DataClass{}}
{}
DeprivedClass::DeprivedClass(const DeprivedClass &value)
{
if ((nullptr != &value) && (this != &value)) {
// Call copy constructor of base class.
BaseClass::BaseClass(value);
deprived_data_ = new DataClass(*(value.deprived_data_));
}
}
DeprivedClass::~DeprivedClass()
{
if (nullptr != deprived_data_) {
delete deprived_data_;
deprived_data_ = nullptr;
}
// Implicit call destructor of base class.
}
DeprivedClass &DeprivedClass::operator =(const DeprivedClass &value)
{
if (this == &value) {
return *this;
}
if (nullptr != &value) {
// Call assignment operator of base class.
BaseClass::operator =(value);
// Avoid memory leak.
if (nullptr != deprived_data_) {
delete deprived_data_;
deprived_data_ = nullptr;
}
// 对DeprivedClass中的字段进行赋值。
deprived_data_ = new DataClass(*(value.deprived_data_));
}
return *this;
}
测试运行代码:
DeprivedClass* d01 = new DeprivedClass{};
DeprivedClass d02(*d01);
d02.deprived_data_->name_ = "aaa";
DeprivedClass* d03 = new DeprivedClass{};
*d03 = d02;
qDebug()<< d03->deprived_data_->name_ << endl;
qDebug()<< d03->deprived_data_ << endl;
qDebug()<< &(d02.deprived_data_) << endl;
运行结果:
"aaa"
DataClass(0x1caeaf4f760)
0x1000ff5e0