如何克隆包含指针的对象?

Joa*_*les 3 c++ oop class

我有个问题.我需要克隆包含指针的对象类.问题的一个示例在以下代码中:

#include "stdafx.h"
#include <iostream>
#include <string.h>
#include <vector>

class CPoint
{
protected:
    int m_x;
    int m_y;

    int *m_p;

public:
    CPoint();
    CPoint(int x, int y);
    ~CPoint();

    CPoint*         clone();
    static CPoint*  clone(CPoint& p);

    int getX();
    int getY();
    void setX(int x);
    void setY(int y);

    void toString();
};

int CPoint::getX()
{
    return m_x;
}

int CPoint::getY()
{
    return m_y;
}

void CPoint::setX( int x )
{
    m_x = x;
}

void CPoint::setY( int y )
{
    m_y = y;
}

void CPoint::toString()
{
    std::cout << "(" << m_x << ", " << m_y<< ", " << *m_p << ")" << std::endl;
}

CPoint::CPoint( int x, int y )
{
    m_x = x;
    m_y = y;    

    m_p = new int();
    *m_p = x + y;
}

CPoint::CPoint()
{
    m_p = new int();
    *m_p = 1000;
}

CPoint* CPoint::clone()
{
    CPoint *p = new CPoint();
    *p = *this;
    return p;
}

CPoint* CPoint::clone( CPoint& p )
{
    CPoint *q = new CPoint();
    *q = p;
    return q;
}

CPoint::~CPoint()
{
    if (m_p) {
        delete m_p;
        m_p = NULL;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    CPoint *p1 = new CPoint(10, 20);
    CPoint *p2 = new CPoint(30, 40);

    p1->toString();
    p2->toString();

    CPoint *p3;
    p3 = CPoint::clone(*p1);

    p3->toString();

    CPoint *p4;
    p4 = p2->clone();

    p4->toString();

    p1->setX(50);
    p1->setY(60);
    p2->setX(80);
    p2->setY(90);

    p3->toString();
    p4->toString();

    delete p1;
    delete p2;
    delete p3;
    delete p4;

    int a;
    std::cin >> a;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我对变量的问题m_p.当克隆对象p1p2on p3p4,内存地址p1p3不同但m_p地址相同时.显然,删除时p1,p3删除失败.有p2p4是一样的.

我如何克隆CPoint类对象?

Mar*_*ork 5

您似乎将其他Java语言的规则应用于C++.
这是一个根本问题,从长远来看会导致各种各样的问题.

你需要学习C++的习语.

在C++中,您希望使用C++字符串(std :: string)而不是C-String接口.

#include <string.h>   // C-Interface

// What you really want
#include <string>     // C++ Interface
Run Code Online (Sandbox Code Playgroud)

如果你的类包含指针,那么你可能做错了什么.RAW指针应包装在智能指针(或容器)中,以正确控制其寿命.如果你把一个指针放到一个商业类中,你就会打破分离关注原则.

class CPoint
{
    protected:
        int m_x;
        int m_y;

        int *m_p;  // What is it supposed to be?
                   // Who owns it?
Run Code Online (Sandbox Code Playgroud)

由于你的班级有一个指针,它打破了三个规则.
如果你想管理这个类中的指针(你没有(打破关注点分离))那么你应该实现三个规则(C++ 11中的五条规则)(查找它).如果你想学习如何处理RAW指针在这里看/sf/answers/129248661/

不需要克隆方法.这就是复制构造函数的用途.你不是在写一个需要克隆的类(否则它会有一个虚拟的析构函数).您的类不是多态的,不会派生自.因此,复制构造函数将完美地工作.

CPoint*         clone();
static CPoint*  clone(CPoint& p);

// Copy constructor looks like this:
CPoint(CPoint const& rjs)

// Assignment operator looks like this:
CPoint& operator=(CPoint& rhs)
Run Code Online (Sandbox Code Playgroud)

但是,如果将RAW指针正确地包装在适当的类中,则不需要这样做.编译器生成的这些方法的默认版本将正常工作.

完全破坏封装的好方法.

int getX();
int getY();
void setX(int x);
void setY(int y);
Run Code Online (Sandbox Code Playgroud)

串!船尾.你真正想要的是序列化方法.

void toString();

// serializer look like this:

friend std::ostream& operator<<(std::ostream& stream, CPoint const& data)
{
     // Convert CPoint (data) to the stream.
     return stream;
}
Run Code Online (Sandbox Code Playgroud)

在C++中,除非我们需要,否则我们不会动态创建对象.
在这里你不需要.创建本地对象的效果更好,因为即使存在异常,它们的生命周期也能得到保证.

// Rather than dynamically creating them
CPoint *p1 = new CPoint(10, 20);
CPoint *p2 = new CPoint(30, 40);

// Just declare two local variables:
CPoint  p1 = CPoint(10, 20);
CPoint  p2(30, 40);           // Alternative to the above but means the same.

// Much better to use operator<<
// Also shows the functions are badly named. You are not converting to string.
// but rather printing them to a stream.
p1->toString();
p2->toString();

std::cout << p1;
myFileStream << p2;  // allows you to easily specify the actual stream.
Run Code Online (Sandbox Code Playgroud)

复制构造函数对于复制对象更有效

CPoint *p3;
p3 = CPoint::clone(*p1);

// If we were still using pointers. 
CPoint* p3 = new CPoint(p1);

// But much nicer to not even use pointers
CPoint  p3(p1);
Run Code Online (Sandbox Code Playgroud)

如果您在功能中看到手动调用删除,通常会出现设计错误.

delete p1;
delete p2;
delete p3;
delete p4;
Run Code Online (Sandbox Code Playgroud)

如果你有指针将它们包装在智能指针(或容器)中,那么类就会使它们异常安全.这是因为对于本地对象,保证析构函数被调用,因此当对象超出范围时,对象将正确删除指针.目前,此代码不是异常安全的,如果异常传播传递它们将泄漏.

小记:main()很特别.如果您没有指定返回值,则编译器会return 0;为您选择.如果您的应用程序没有错误状态,最好使用此功能作为对其他开发人员的标志,您的代码将始终干净地退出.

return 0;
Run Code Online (Sandbox Code Playgroud)

我会像这样重写:

#include <iostream>
#include <string>
#include <vector>

class CPoint
{
protected:
    int m_x;
    int m_y;

    std::vector<int> m_p;

public:
    // If you don't explicitly initialize m_x and m_y them
    // they will have indeterminate (random) values.
    CPoint()             : m_x(0), m_y(0) {m_p.push_back(1000);}
    CPoint(int x, int y) : m_x(x), m_y(y) {m_p.push_back(x + y);}

    int getX()        { return m_x;}
    int getY()        { return m_y;}
    void setX(int x)  { m_x = x;}
    void setY(int y)  { m_y = y;}

    friend std::ostream& operator<<(std::ostream& stream, CPoint const& d)
    {
        return stream << "(" << d.m_x << ", " << d.m_y<< ", " << d.m_p[0] << ")" << std::endl;
    }
};




int main(int argc, char* argv[])
{
    CPoint p1(10, 20);
    CPoint p2(30, 40);

    std::cout << p1 << p2;

    CPoint p3(p1);

    std::cout << p3;

    CPoint p4(p2);
    std::cout << p4;

    p1.setX(50);
    p1.setY(60);
    p2.setX(80);
    p2.setY(90);

    std::cout << p1 << p2 << p3 << p4;
    int a;
    std::cin >> a;
}
Run Code Online (Sandbox Code Playgroud)