The rule of three/five/zero
2021-09-05 本文已影响0人
greatseniorsde
一个例子Chatbot:
#ifndef CHATBOT_H_
#define CHATBOT_H_
#include <wx/bitmap.h>
#include <string>
class GraphNode; // forward declaration
class ChatLogic; // forward declaration
class ChatBot
{
private:
wxBitmap *_image; // avatar image
GraphNode *_rootNode;
ChatLogic *_chatLogic;
public:
// constructors / destructors
ChatBot(); // constructor WITHOUT memory allocation
ChatBot(std::string filename); // constructor WITH memory allocation
~ChatBot();
ChatBot(const ChatBot &source); // copy constructor
ChatBot &operator=(const ChatBot &source); // copy assignment operator
ChatBot(ChatBot &&source); // move constructor
ChatBot &operator=(ChatBot &&source); // move assignment operator
};
#endif /* CHATBOT_H_ */
#include <iostream>
#include <random>
#include <algorithm>
#include <ctime>
#include "chatlogic.h"
#include "graphnode.h"
#include "graphedge.h"
#include "chatbot.h"
// constructor WITHOUT memory allocation
ChatBot::ChatBot()
{
// invalidate data handles
_image = nullptr;
_chatLogic = nullptr;
_rootNode = nullptr;
}
// constructor WITH memory allocation
ChatBot::ChatBot(std::string filename)
{
std::cout << "ChatBot Constructor" << std::endl;
// invalidate data handles
_chatLogic = nullptr;
_rootNode = nullptr;
// load image into heap memory
_image = new wxBitmap(filename, wxBITMAP_TYPE_PNG);
}
ChatBot::~ChatBot()
{
std::cout << "ChatBot Destructor" << std::endl;
// deallocate heap memory
if(_image != NULL) // Attention: wxWidgets used NULL and not nullptr
{
delete _image;
_image = NULL;
}
}
ChatBot::ChatBot(const ChatBot &source) // 2 : copy constructor
{
std::cout << "ChatBot Copy Constructor" << std::endl;
_rootNode = source._rootNode;
_chatLogic = source._chatLogic;
_image = source._image;
}
ChatBot& ChatBot::operator=(const ChatBot &source) // 3 : copy assignment operator
{
std::cout << "ChatBot Copy Assignment" << std::endl;
if (this == &source)
return *this;
_rootNode = source._rootNode;
_chatLogic = source._chatLogic;
_image = source._image;
return *this;
}
ChatBot::ChatBot(ChatBot &&source) // 4 : move constructor
{
std::cout << "ChatBot Move Constructor " << this << std::endl;
_image = source._image;
_chatLogic = source._chatLogic;
_chatLogic->SetChatbotHandle(this);
_rootNode = source._rootNode;
source._image = nullptr;
source._chatLogic = nullptr;
source._rootNode = nullptr;
}
ChatBot& ChatBot::operator=(ChatBot &&source) // 5 : move assignment operator
{
std::cout << "ChatBot Move Assignment Operator " << this << std::endl;
if (this == &source)
return *this;
delete _image;
_image = source._image;
_chatLogic = source._chatLogic;
_chatLogic->SetChatbotHandle(this);
_rootNode = source._rootNode;
source._rootNode = nullptr;
source._image = nullptr;
source._chatLogic = nullptr;
return *this;
}
}
- Copy constructors/assignment:
Copy constructors are used to initialize a class by making a copy of an object of the same class. Copy assignment is used to copy one class to another existing class. By default, C++ will provide a copy constructor and copy assignment operator if one is not explicitly provided. These compiler-provided functions do shallow copies, which may cause problems for classes that allocate dynamic memory. So classes that deal with dynamic memory should override these functions to do deep copies.
所以这里的copy constuctor/assignment里对于_image的copy是shallow copy, 只是复制了指针,而没有复制指针指向的address上的数据
You need to use a deep copy here.A deep copy copies all fields and makes copies of dynamically allocated memory pointed to by the fields.
*_image = *source._image; // deep copy
浅拷贝导致的结果是可能在call dtor的时候两次destroy同一内存地址的数据
对于这类问题的解决办法一是自己定义deep copy in copy constructor/assignment, 也有人会直接禁用copy constructor/assignment, 于是我看到了这篇文章:
为什么很多人禁用拷贝(复制)构造函数
- The rule of three/five/zero
According to CppCoreGuidelines:
C.20: If you can avoid defining default operations, do
C.21: If you define or =delete
any copy, move, or destructor function, define or =delete
them all
Back to Basics: RAII and the Rule of Zero - Arthur O'Dwyer - CppCon 2019