c++ - 如何对对象执行深拷贝?你如何制作复制构造函数?

标签 c++ copy copy-constructor deep-copy c++03

#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 约束以确保拷贝(以及对拷贝的更改)不会反射(reflect)在它所基于的原始文件上?

在代码中,我定义了 explicitDest(),这是我显式(且仅显式)调用析构函数的方式,因为我只是有时需要某些行为。以防万一有人问。

如果代码不明显,我对复制、复制构造函数或抽象类/方法不是很熟悉。

最佳答案

您应该实现复制构造函数和复制赋值运算符,并在使用 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、移动语义和扩展添加了初始化列表。

关于c++ - 如何对对象执行深拷贝?你如何制作复制构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59369230/

相关文章:

java - Gradle 复制任务 : How to overwrite existing files?

c++ - 错误 : templated copy constructor calls itself

c++ - 返回指向私有(private)成员的指针的公共(public)方法的单元测试

c++ - boost::bind protected 成员和上下文

c++ - 错误 : not. exe 已停止工作 - 尝试构建 LLVM 时

C++ 复制构造函数和 STL 映射

c++ - 2种不同类型的构造函数从复制构造函数调用

c++ - 为什么我们需要安置新的运营商?

if-statement - 批处理文件检查目录是否存在,如果不存在则使用备用目录进行文件复制

java - 如何在保持引用一致的情况下复制对象?