簡體   English   中英

如何對對象執行深拷貝? 你如何制作復制構造函數?

[英]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() ,這是我顯式(並且僅顯式)調用析構函數的方式,因為我有時只需要某些行為。 以防萬一人們問。

如果代碼不明顯,我對復制、復制構造函數或抽象類/方法並不十分熟悉。

您應該實現復制構造函數和復制賦值運算符,並在使用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 、移動語義和擴展初始化列表的示例中,有很多事情會以稍微不同(更有效)的方式完成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM