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

classes with pointer members and no overridden copy constructor

本文关键字:复制 构造函数 重写 指针 成员      更新时间:2023-10-16

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

这是否意味着在其他类实例中有其他对象已经创建,但没有指向?

作为一个侧面的问题,我认为一个共享指针会指向所有的类在一组对象,但在一个安全的方式是正确的吗?

是-当您没有定义复制构造函数时,编译器将为您发出一个-它将做一个浅复制-只是复制值(i。(指针的地址)

所以这两个对象(original和'copy')将有指向同一个对象的指针字段

如果你不深度复制对象,也就是说,如果你不覆盖复制函数并进行浅复制,指针将指向同一个对象的实例。如果随后删除其中一个浅拷贝对象,则其他对象的指针将指向垃圾。如果您以任何方式取消引用它们,您的程序将崩溃。

赋值操作符也会发生同样的情况。当你有指针时,重载它们。

一个例子:

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::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来避免所有这些事情-这只是一个概念证明的例子。

假设您有一个这样的类,它显示了您在问题中提出的问题

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 &);
};
替代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 (BEST)

使用c++ 11 shared_ptr或boost并省略复制和赋值作为默认编译器,前提是它会做正确的事情,因为shared_ptr被refcount,并且即使x和y共享相同的Foo,也只会删除Foo一次。还需要注意的是,~Bar不需要显式清理,因为当Foo的refcount变为零时,mFoo将在std::shared_ptr<Foo>析构函数中自动删除。

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

让代码来处理:

struct X
{
    int data;
    int *ptr;
    X() : ptr(&data) {}
};
X a;
X b = a; // yes, `a.ptr` points to `b.data`!

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

使用pointer-to-members

可以解决的:

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++的理解)