如何对对象执行深拷贝?你如何制作复制构造函数?
How do you perform a deep copy on an object? How do you make a copy constructor?
#include <iostream>
class Piece {
public:
virtual char get()=0;
virtual ~Piece() {};
};
class One : public Piece {
public:
char get() { return '1'; }
};
class Two : public Piece {
public:
char get() { return '2'; }
};
class Tile {
private:
Piece* occ;
bool prs;
public:
Tile() { prs = false; }
void setOcc(Piece* p) { prs = true; occ = p; }
Piece& getOcc() { return *occ; }
bool getPrs() { return prs; }
void explicitDest() { if (prs) { delete occ; prs = false; } }
};
class Board {
private:
Tile tiles[2][2];
public:
Board() {
tiles[0][0].setOcc(new One());
tiles[0][1].setOcc(new Two());
tiles[1][1].setOcc(new One());
}
Tile getTile(int c, int r) { return tiles[c][r]; }
void move(Board* b, int c1, int r1, int c2, int r2) {
switch(b->tiles[c1][r1].getOcc().get()) {
case '1': b->tiles[c2][r2].setOcc(new One()); break;
case '2': b->tiles[c2][r2].setOcc(new Two()); break;
}
b->tiles[c1][r1].explicitDest();
}
void print() {
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
if (tiles[j][i].getPrs()) {
std::cout << tiles[j][i].getOcc().get() << " ";
} else {
std::cout << "- ";
}
}
std::cout << "\n";
}
std::cout << "\n";
}
Board* copyBoard() { return new Board(*this); }
};
int main()
{
Board* oldBoard = new Board();
std::cout << "Old board: \n";
oldBoard->print();
Board* newBoard = oldBoard->copyBoard();
std::cout << "New board: \n";
newBoard->print();
newBoard->move(newBoard, 0, 0, 1, 1);
std::cout << "Old board after move: \n";
oldBoard->print();
std::cout << "New board after move: \n";
newBoard->print();
delete[] newBoard;
}
这是一个 MRE,用于说明我一直用来进行深度复制的方法。它并不完全有效,只是为了形象化我一直在做的事情。
用这个例子,我的深拷贝方法强吗?如果不是,有哪些方法可用于 C++03 约束以确保副本(以及对副本的更改)不会反映在它所基于的原始文件上?
在代码中,我定义了 explicitDest()
这是我显式(且仅显式)调用析构函数的方式,因为我只是有时需要某些行为。以防万一有人问。
如果代码不明显,我对复制、复制构造函数或抽象classes/methods不是很熟悉。
您应该实现复制构造函数和复制赋值运算符,并在使用时小心 new
。每个 new
都需要一个 delete
- 除非你将 new
返回的指针交给智能指针。在 C++03 中,您有 std::auto_ptr
可用于为您管理内存资源。
这是一个包含内联注释的示例:
#include <iostream>
#include <memory> // std::auto_ptr
#include <algorithm> // std::swap (<algorithm> in c++03, <utility> in >= c++11)
class Piece {
public:
// A virtual destructor to support deleting via base class pointer:
virtual ~Piece() {}
// You can't make constructors virtual, so add a clone()
// function for copy constuction through a base class pointer
virtual std::auto_ptr<Piece> clone() const = 0;
// renamed get() into symbol()
virtual char symbol() const = 0;
};
class One : public Piece {
public:
// Use the implicit copy constructor for One and return a (smart) pointer
// to the base class.
std::auto_ptr<Piece> clone() const {
return std::auto_ptr<Piece>(new One(*this));
}
char symbol() const { return '1'; }
};
class Two : public Piece {
public:
std::auto_ptr<Piece> clone() const {
return std::auto_ptr<Piece>(new Two(*this));
}
char symbol() const { return '2'; }
};
class Tile {
private:
std::auto_ptr<Piece> occ; // this now handles delete for you
public:
Tile() : occ(NULL) {} // default constructor
Tile(Piece* p) : occ(p) {} // put pointer in auto_ptr
// copy constructor, use the clone() function and conversion
// to bool operator below. If "o" doesn't have a Piece, initialize occ
// with an default constructed, empty, auto_ptr<Piece>.
Tile(const Tile& o) : occ(o ? o.occ->clone() : std::auto_ptr<Piece>()) {}
// ^
// |
// +--- conversion to bool in use
// copy assignment operator
Tile& operator=(const Tile& o) {
Tile tmp(o); // use the copy constructor above
occ = tmp.occ; // steal pointer from tmp
return *this;
}
// converting assignment operator
Tile& operator=(Piece* p) {
// delete the old pointer and replace it with p:
occ.reset(p);
return *this;
}
// Conversion to bool operator using std::auto_ptr's built in get()
// to tell us if we have a Piece or not.
operator bool() const { return occ.get() != NULL; }
// Add a symbol() function to hide the logic to determine if this Tile
// has a Piece or not.
char symbol() const {
// Check if there is a Piece in this Tile using the conversion
// to bool operator here too:
if(*this)
return occ->symbol();
else
return '-'; // no Piece here
}
};
// add support to stream a Tile to an ostream
std::ostream& operator<<(std::ostream& os, const Tile& t) {
return os << t.symbol();
}
class Board {
private:
Tile tiles[2][2];
public:
Board() {
// using the added operator() further down
(*this)(0,0) = new One;
(*this)(0,1) = new Two;
(*this)(1,1) = new One;
}
// Note that cols and rows in arrays are usually seen as reversed.
// tiles[2][2] usually means:
// tiles[<rows>=2][<cols>=2]
// getTile() replacements - the interface here is still (col, row)
// but it accesses the tiles[][] using the common form (row, col)
Tile& operator()(int c, int r) { return tiles[r][c]; }
Tile const& operator()(int c, int r) const { return tiles[r][c]; }
// moving by swapping tiles
void move(int c1, int r1, int c2, int r2) {
// using operator() and the standard function std::swap
std::swap((*this)(c1, r1), (*this)(c2, r2));
}
};
// Add a stream operator to not have to call print() explicitly when streaming
std::ostream& operator<<(std::ostream& os, const Board& b) {
for(int r = 0; r < 2; r++) {
for(int c = 0; c < 2; c++) {
// Use "Board::operator() const" and stream support for returned
// Tile.
os << b(c, r);
}
os << '\n';
}
os << '\n';
return os;
}
int main() {
// no need to "new" anything:
Board oldBoard;
Board newBoard(oldBoard); // use copy constructor
// use streaming operators
std::cout << "Old board: \n" << oldBoard;
std::cout << "New board: \n" << newBoard;
// using the getTile() replacement, Board::operator():
std::cout << "New board @ tile 1,0: " << newBoard(1, 0) << " before move\n";
newBoard.move(0, 0, 1, 0);
std::cout << "New board @ tile 1,0: " << newBoard(1, 0) << " after move\n\n";
std::cout << "New board after move:\n" << newBoard;
newBoard = oldBoard; // copy assignment operator
std::cout << "New board after reinit:\n" << newBoard;
}
请注意,示例中有许多内容在 C++11 及更高版本中会以略有不同(更高效)的方式完成,其中添加了 std::unique_ptr
、移动语义和扩展初始化列表.
#include <iostream>
class Piece {
public:
virtual char get()=0;
virtual ~Piece() {};
};
class One : public Piece {
public:
char get() { return '1'; }
};
class Two : public Piece {
public:
char get() { return '2'; }
};
class Tile {
private:
Piece* occ;
bool prs;
public:
Tile() { prs = false; }
void setOcc(Piece* p) { prs = true; occ = p; }
Piece& getOcc() { return *occ; }
bool getPrs() { return prs; }
void explicitDest() { if (prs) { delete occ; prs = false; } }
};
class Board {
private:
Tile tiles[2][2];
public:
Board() {
tiles[0][0].setOcc(new One());
tiles[0][1].setOcc(new Two());
tiles[1][1].setOcc(new One());
}
Tile getTile(int c, int r) { return tiles[c][r]; }
void move(Board* b, int c1, int r1, int c2, int r2) {
switch(b->tiles[c1][r1].getOcc().get()) {
case '1': b->tiles[c2][r2].setOcc(new One()); break;
case '2': b->tiles[c2][r2].setOcc(new Two()); break;
}
b->tiles[c1][r1].explicitDest();
}
void print() {
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
if (tiles[j][i].getPrs()) {
std::cout << tiles[j][i].getOcc().get() << " ";
} else {
std::cout << "- ";
}
}
std::cout << "\n";
}
std::cout << "\n";
}
Board* copyBoard() { return new Board(*this); }
};
int main()
{
Board* oldBoard = new Board();
std::cout << "Old board: \n";
oldBoard->print();
Board* newBoard = oldBoard->copyBoard();
std::cout << "New board: \n";
newBoard->print();
newBoard->move(newBoard, 0, 0, 1, 1);
std::cout << "Old board after move: \n";
oldBoard->print();
std::cout << "New board after move: \n";
newBoard->print();
delete[] newBoard;
}
这是一个 MRE,用于说明我一直用来进行深度复制的方法。它并不完全有效,只是为了形象化我一直在做的事情。
用这个例子,我的深拷贝方法强吗?如果不是,有哪些方法可用于 C++03 约束以确保副本(以及对副本的更改)不会反映在它所基于的原始文件上?
在代码中,我定义了 explicitDest()
这是我显式(且仅显式)调用析构函数的方式,因为我只是有时需要某些行为。以防万一有人问。
如果代码不明显,我对复制、复制构造函数或抽象classes/methods不是很熟悉。
您应该实现复制构造函数和复制赋值运算符,并在使用时小心 new
。每个 new
都需要一个 delete
- 除非你将 new
返回的指针交给智能指针。在 C++03 中,您有 std::auto_ptr
可用于为您管理内存资源。
这是一个包含内联注释的示例:
#include <iostream>
#include <memory> // std::auto_ptr
#include <algorithm> // std::swap (<algorithm> in c++03, <utility> in >= c++11)
class Piece {
public:
// A virtual destructor to support deleting via base class pointer:
virtual ~Piece() {}
// You can't make constructors virtual, so add a clone()
// function for copy constuction through a base class pointer
virtual std::auto_ptr<Piece> clone() const = 0;
// renamed get() into symbol()
virtual char symbol() const = 0;
};
class One : public Piece {
public:
// Use the implicit copy constructor for One and return a (smart) pointer
// to the base class.
std::auto_ptr<Piece> clone() const {
return std::auto_ptr<Piece>(new One(*this));
}
char symbol() const { return '1'; }
};
class Two : public Piece {
public:
std::auto_ptr<Piece> clone() const {
return std::auto_ptr<Piece>(new Two(*this));
}
char symbol() const { return '2'; }
};
class Tile {
private:
std::auto_ptr<Piece> occ; // this now handles delete for you
public:
Tile() : occ(NULL) {} // default constructor
Tile(Piece* p) : occ(p) {} // put pointer in auto_ptr
// copy constructor, use the clone() function and conversion
// to bool operator below. If "o" doesn't have a Piece, initialize occ
// with an default constructed, empty, auto_ptr<Piece>.
Tile(const Tile& o) : occ(o ? o.occ->clone() : std::auto_ptr<Piece>()) {}
// ^
// |
// +--- conversion to bool in use
// copy assignment operator
Tile& operator=(const Tile& o) {
Tile tmp(o); // use the copy constructor above
occ = tmp.occ; // steal pointer from tmp
return *this;
}
// converting assignment operator
Tile& operator=(Piece* p) {
// delete the old pointer and replace it with p:
occ.reset(p);
return *this;
}
// Conversion to bool operator using std::auto_ptr's built in get()
// to tell us if we have a Piece or not.
operator bool() const { return occ.get() != NULL; }
// Add a symbol() function to hide the logic to determine if this Tile
// has a Piece or not.
char symbol() const {
// Check if there is a Piece in this Tile using the conversion
// to bool operator here too:
if(*this)
return occ->symbol();
else
return '-'; // no Piece here
}
};
// add support to stream a Tile to an ostream
std::ostream& operator<<(std::ostream& os, const Tile& t) {
return os << t.symbol();
}
class Board {
private:
Tile tiles[2][2];
public:
Board() {
// using the added operator() further down
(*this)(0,0) = new One;
(*this)(0,1) = new Two;
(*this)(1,1) = new One;
}
// Note that cols and rows in arrays are usually seen as reversed.
// tiles[2][2] usually means:
// tiles[<rows>=2][<cols>=2]
// getTile() replacements - the interface here is still (col, row)
// but it accesses the tiles[][] using the common form (row, col)
Tile& operator()(int c, int r) { return tiles[r][c]; }
Tile const& operator()(int c, int r) const { return tiles[r][c]; }
// moving by swapping tiles
void move(int c1, int r1, int c2, int r2) {
// using operator() and the standard function std::swap
std::swap((*this)(c1, r1), (*this)(c2, r2));
}
};
// Add a stream operator to not have to call print() explicitly when streaming
std::ostream& operator<<(std::ostream& os, const Board& b) {
for(int r = 0; r < 2; r++) {
for(int c = 0; c < 2; c++) {
// Use "Board::operator() const" and stream support for returned
// Tile.
os << b(c, r);
}
os << '\n';
}
os << '\n';
return os;
}
int main() {
// no need to "new" anything:
Board oldBoard;
Board newBoard(oldBoard); // use copy constructor
// use streaming operators
std::cout << "Old board: \n" << oldBoard;
std::cout << "New board: \n" << newBoard;
// using the getTile() replacement, Board::operator():
std::cout << "New board @ tile 1,0: " << newBoard(1, 0) << " before move\n";
newBoard.move(0, 0, 1, 0);
std::cout << "New board @ tile 1,0: " << newBoard(1, 0) << " after move\n\n";
std::cout << "New board after move:\n" << newBoard;
newBoard = oldBoard; // copy assignment operator
std::cout << "New board after reinit:\n" << newBoard;
}
请注意,示例中有许多内容在 C++11 及更高版本中会以略有不同(更高效)的方式完成,其中添加了 std::unique_ptr
、移动语义和扩展初始化列表.