具有指针成员的类,没有重写的复制构造函数

时间:2011-12-06 14:08:13

标签: c++ pointers copy-constructor

当指针指向在同一个类中声明的内容时,我是否正确地认为如果复制这样一个对象,即有多组指针,但它们都指向同一个对象?

这是否意味着其他类实例中还有其他对象已创建但没有任何指向?

作为一个附带问题,我是否正确地认为共享指针会将所有类指向一组对象但是以安全的方式?

4 个答案:

答案 0 :(得分:3)

是 - 当你没有定义一个拷贝构造函数时,编译器会为你发一个 - 这将做一个浅拷贝 - 只需复制指针的值(即地址)。

因此,两个对象(原始和'copy')将指针字段指向同一个对象。

答案 1 :(得分:2)

如果不深度复制对象,即如果不覆盖复制c'tor并执行浅复制,则指针将指向对象的同一实例。如果然后删除其中一个浅复制对象,则其他对象的指针将指向垃圾。如果您以任何方式取消引用它们,程序将崩溃。

赋值运算符也会发生同样的事情。所以,当你有指针超载它们时。

一个例子:

struct Message
{
    Message(const LogType_E & type_in = LOG_ERROR, const unsigned int & domain_in = 0, const int & msgId_in = 0, const char * msg_in = "");
    int myMsgID;            //!< message id
    unsigned int myDomain;  //!< message domain
    LogType_E myType;       //!< message type
    char * myMsg;           //!< actual message 

    ~Message()
    {
        if(myMsg != NULL) delete [] myMsg;
    }

    Message(const Message &);

    const Message& operator=(const Message & rhs);
};

这是一种“消息”类型,用于保存带有其他内容的消息。

实施看起来像:

Message::Message(const Message & cp_in):myType(cp_in.myType), myDomain(cp_in.myDomain), myMsgID(cp_in.myMsgID), myMsg(NULL)
{
    if(cp_in.myMsg != NULL)
    {
        myMsg = new char[strlen(cp_in.myMsg)+1];
        memcpy (myMsg, cp_in.myMsg, strlen(cp_in.myMsg)+1); 
    }
}

const Message & Message::operator =(const AX::Base::Log::Message &cp_in)
{
    if (this == &cp_in) // protect against invalid self-assignment
        return *this;

    //deallocate old memory
    if(myMsg != NULL) delete [] myMsg;

    if(cp_in.myMsg != NULL)
    {
    //allocate new memory and copy the elements
        myMsg = new char[strlen(cp_in.myMsg)+1];
        memcpy (myMsg, cp_in.myMsg, strlen(cp_in.myMsg)+1); 
    }

    // copy other data members
    myType = cp_in.myType;
    myDomain = cp_in.myDomain;
    myMsgID = cp_in.myMsgID;

    return *this;
}

那就是说,请使用std::string来避免所有这些事情 - 这只是一个概念证明示例。

答案 2 :(得分:1)

让代码进行讨论以清除问题:

struct X
{
    int data;
    int *ptr;

    X() : ptr(&data) {}
};

X a;
X b = a; // yes, `a.ptr` points to `b.data`!

实际上,指针将被逐字复制并将继续指向副本的

使用指向成员的指针

因此可修复:

struct X
{
    int data;
    int X::*ptr;

    X() : ptr(&X::data) {}
};

X a;
X b = a; // now, `a.ptr` points to `a.data`

使用更多使用提示 https://ideone.com/F0rC3

扩展此示例
a.ptr = &X::data2;  // now `a.ptr` points to `a.data2`
                    //     `b.ptr` points to `b.data1`
b = a;              //     `b.ptr` points to `b.data2` too

// Usage hint:
int deref = a.*(a.ptr); // gets the field pointed to by a.ptr, from the instance a
    deref = b.*(b.ptr); // gets the field pointed to by b.ptr, from the instance b

// but of course you could get fancy and do
    deref = a.*(b.ptr); // gets the field pointed to by b.ptr, **but** from the instance a

那会做你想要的。虽然,为什么你希望超出我的范围(可能超出C ++)

答案 3 :(得分:1)

想象一下,你有一个类似的课程,展示你在问题中提出的问题

class Foo{};

class Bar
{
public:
    Foo* mFoo;
    Bar() : mFoo( new Foo() ) {}
    ~Bar() { delete mFoo;}
};

和代码一样

Bar x ;
Bar y = x;

以上代码将导致核心转储,因为y和x都指向相同的 Foo ,析构函数将尝试删除相同的 Foo 两次。

替代方案1

声明但不提供定义,以便Bar永远不会复制构造函数或分配。这将确保Bar y = x会出现链接错误,因为您设计的类不被复制。

class Bar
{
public:
    Foo* mFoo;
    Bar() : mFoo( new Foo() ) {}
    ~Bar() { delete mFoo;}

    Bar(const Bar &);
    Bar& operator= (const Bar &);
};

ALTERNATIVE 2

提供执行正确操作的复制构造函数和赋值运算符。而不是编译器提供执行浅拷贝的复制和赋值的默认实现,而是复制 Foo ,以便x和y都有自己的 Foo

class Bar
{
public:
    Foo* mFoo;
    Bar() : mFoo( new Foo() ) {}
    ~Bar() { delete mFoo;}

    Bar(const Bar & src)
    {
        mFoo = new Foo( *(src.mFoo) );
    }

    Bar& operator= (const Bar & src)
    {
        mFoo = new Foo( *(src.mFoo) );
        return *this;
    }
};

替代3(最佳)

使用C ++ 11 shared_ptr或者提升并省略复制和分配作为默认编译器提供一个正确的事情,因为 shared_ptr 被重新计算并将删除 Foo 即使x和y共享相同的 Foo ,也只有一次。另请注意,~Bar无需明确清理,因为当{strong> Foo 的引用计数变为零时,mFoo析构函数中将自动删除std::shared_ptr<Foo>

class Bar
{
public:
    std::shared_ptr<Foo> mFoo;
    Bar() :mFoo( new Foo() ) {}
    ~Bar() { }
};
相关问题