我有一個機器人類,它有一個整數指標向量(用于存盤完成的作業歷史),但是當我將一個機器人的物件復制到另一個機器人并且第一個機器人超出范圍時,然后我列印機器人的歷史記錄給了我一大堆亂數。我試過制作我自己的復制建構式并按值將 _history 設定為新物件 _history 值,但給出了相同的回應。
ROBOT.h
# pragma once
#include <iostream>
#include <vector>
class Robot{
private:
int workUnit = 0;
std::vector<int>* _history; // pointer to vector of ints (NOT a vector of int pointers)
public:
Robot() : name("DEFAULT") {_history = new std::vector<int>();};
Robot(const std::string& name) : name(name){_history = new std::vector<int>();};
~Robot(){std::cout << name << ": Goodbye!" << std::endl; delete _history;};
std::string whoAmI() const {return name;};
void setName(const std::string& name){this->name = name;};
void work();
void printWork() const;
std::vector<int>* getHistory() const { return _history; };
protected:
std::string name;
};
機器人.cpp
# include "Robot.h"
void Robot::work(){
workUnit ;
_history -> push_back(workUnit);
std::cout << name << " is working. > " << workUnit <<"\n";
}
void Robot::printWork() const {
std::cout << "Robot " << name << " has done the following work: ";
for(const int& record : *_history){
std::cout << record << " ";
}
std::cout << std::endl;
}
主要的
#include <iostream>
#include "Robot.h"
int main(){
Robot r2("Task5 Robo");
{
Robot r1("r1");
r1.whoAmI();
r1.work();
r1.work();
r1.printWork();
std::cout << "assign r1 to r2..." << std::endl;
r2 = r1;
r2.setName("r2");
r2.whoAmI();
r2.printWork();
}
r2.whoAmI();
r2.printWork();
std::cout << "end of example code..." << std::endl;
return 0;
}
輸出我得到:
r1 is working. > 1
r1 is working. > 2
Robot r1 has done the following work: 1 2
assign r1 to r2...
Robot r2 has done the following work: 1 2
r1: Goodbye!
Robot r2 has done the following work: 7087248 0 6975376 0 0 0 -1124073283 19523 7087248 0 6975408 0 7087248 0 6947152 0 0 -1 -1174404934 19523 7087248 0 6947152 0 1701603654 1917803635 1701602145 1986087516 1634360417 (and lots more random numbers)
uj5u.com熱心網友回復:
下面是一個示例,說明如何實作 5規則中的五個特殊成員函式。首先,您的默認建構式和接受字串的建構式可以結合起來,以便默認建構式委托給接受字串的建構式:
Robot(const std::string& name) :
_history(new std::vector<int>()),
name(name)
{};
Robot() : Robot("DEFAULT") {} // Delegate
這是五項規則:
// --- rule of five ---
Robot(const Robot& rhs) : // copy constructor
workUnit(rhs.workUnit),
_history(new std::vector<int>(*rhs._history)),
name(rhs.name)
{}
Robot(Robot& rhs) noexcept : // move constructor
workUnit(rhs.workUnit),
// use exchange to steal pointer and replace with nullptr:
_history(std::exchange(rhs._history, nullptr)),
name(std::move(rhs.name))
{}
Robot& operator=(const Robot& rhs) { // copy assignment operator
workUnit = rhs.workUnit;
*_history = *rhs._history; // use vector's copy assignment operator
name = rhs.name;
return *this;
}
Robot& operator=(Robot&& rhs) noexcept { // move assignment operator
workUnit = rhs.workUnit;
// swap pointers, let rhs destroy *this old pointer:
std::swap(_history, rhs._history);
name = std::move(rhs.name);
return *this;
}
~Robot() { // destructor
std::cout << name << ": Goodbye!\n";
delete _history;
};
// --- rule of five end ---
當您處理原始擁有指標時,您可以使用 astd::unique_ptr來免費獲取其中的一些。代替
std::vector<int>* _history;
你做到了:
std::unique_ptr<std::vector<int>> _history;
所以這個類變成了:
Robot(const std::string& name) :
_history(std::make_unique<std::vector<int>>()),
name(name)
{};
Robot() : Robot("DEFAULT") {} // Delegate
五的規則變得更簡單一點:
// --- rule of five ---
Robot(const Robot& rhs) : // copy constructor
workUnit(rhs.workUnit),
_history(std::make_unique<std::vector<int>>(*rhs._history)),
name(rhs.name)
{}
// move constructor handled by unique_ptr, just default it:
Robot(Robot& rhs) noexcept = default;
Robot& operator=(const Robot& rhs) { // copy assignment operator
workUnit = rhs.workUnit;
*_history = *rhs._history;
name = rhs.name;
return *this;
}
// move assignment operator handled by unique_ptr, just default it:
Robot& operator=(Robot&& rhs) noexcept = default;
~Robot() { // destructor
std::cout << name << ": Goodbye!\n";
// delete _history; // no delete needed, unique_ptr does it
};
// --- rule of five end ---
您可能還想_history從getHistory(). 以下內容適用于原始指標和unique_ptr版本,并為您的類提供更好的界面:
const std::vector<int>& getHistory() const { return *_history; };
std::vector<int>& getHistory() { return *_history; };
uj5u.com熱心網友回復:
當你摧毀一個機器人時,你就摧毀了它的作業歷史。當你復制一個機器人時會發生什么,它會得到一個指向作業歷史的指標的副本。換句話說,第二個機器人有一個指標,指向與第一個機器人創建的完全相同的整數向量。
現在,當第一個機器人被摧毀時,它會洗掉它擁有的作業歷史。這就是為什么第二個機器人的作業歷史在列印時無效的原因:記憶體已被釋放。
我可以為此提出兩種可能的解決方案。一種是實作“5 法則”,除其他外,它允許您指定(通過定義復制建構式和賦值運算子)一個機器人如何復制另一個機器人,包括創建作業歷史記錄它將擁有并且不能被第一個機器人洗掉。另一種是使用“共享指標”來管理作業歷史的生命周期。
鑒于作業經歷聽起來不應該由多個機器人共享,我會選擇第一個選項。
uj5u.com熱心網友回復:
r2 = r1;
使用隱式宣告的默認復制賦值運算子。由于默認實作只是簡單地進行成員復制,因此舊_history指標被簡單地覆寫,并且除了r2在分配未正確釋放之前存盤的舊向量之外,該向量意外地由 2 個物件擁有。
您應該實作移動建構式 移動賦值運算子、復制建構式 復制賦值運算子或兩者對。
但是,如果您將_history向量保留為原始指標,則只需執行此操作;更改為std::vector<int>建構式/賦值運算子的默認實作存在且有效,更改為std::unique_ptr<std::vector<int>>將導致復制賦值運算子/復制建構式被洗掉。
注意:對于所有的方法,你應該改變的回傳型別whoAmI,并getHistory在最后一個選項描述。
復制賦值運算子
這在分配后保持兩個物件“完整”。
需要一個自定義實作來正確復制指標。
class Robot{
...
public:
...
Robot(Robot const& other)
: workUnit(other.workUnit), _history(new std::vector<int>(*other._history)), name(other.name)
{}
Robot& operator=(Robot const& other)
{
workUnit = other.workUnit;
*_history = *other._history;
name = other.name;
return *this;
}
...
};
移動分配
這需要您將分配更改為在只有解構式可以保證作業的狀態下r2 = std::move(r1);離開r1。請注意,這樣做您不應該name在解構式中列印,因為r2的名稱已被移出。
class Robot{
...
public:
...
Robot(Robot && other) noexcept
: workUnit(other.workUnit), _history(other._history), name(std::move(other.name))
{
other._history = nullptr; // prevent double free; we're the sole owner of the vector now
}
Robot& operator=(Robot && other) noexcept
{
workUnit = other.workUnit;
delete _history; // old history no longer needed -> free to avoid memory leak
_history = other->_history;
other._history = nullptr; // prevent double free; we're the sole owner of the vector now
name = std::move(other.name);
return *this;
}
...
};
簡單選項(推薦)
使用std::vector<int>默認的建構式:
class Robot{
private:
int workUnit = 0;
std::vector<int> _history; // pointer to vector of ints (NOT a vector of int pointers)
public:
Robot() : name("DEFAULT") {}
Robot(const std::string& name) : name(name){}
~Robot(){std::cout << name << ": Goodbye!" << std::endl; }
Robot(Robot const&) = default;
Robot& operator=(Robot const&) = default;
// don't print name in the destructor, if you uncomment the following 2 members
// Robot(Robot&&) = default;
// Robot& operator=(Robot&&) = default;
std::string const& whoAmI() const {return name;} // user should be able to decide, if a copy is needed
void setName(const std::string& name){this->name = name;}
void work();
void printWork() const;
std::vector<int> const& getHistory() const { return _history; } // don't return raw a pointer here
std::vector<int>& getHistory() { return _history; } // overload only needed, if the history needs to be modifiable from the outside
protected:
std::string name;
};
如果回傳的歷史記錄需要對 const 物件進行修改,請考慮制作_history mutable.
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/364456.html
下一篇:列印類屬性期間的語法錯誤
