通过返回的指针填充私有字段

Fill private fields through returned pointers

本文关键字:字段 填充 指针 返回      更新时间:2023-10-16

我有一个容器类:

class Container {
public:
    MyItem* getItem() { return mItem.get(); }
private:
    std::auto_ptr<MyItem> mItem;
};

我正在尝试编写一个initializeContainer()方法,在其中使用Container.getItem()来设置mItem。以下是一些可能的方法,我在评论中对它们表示怀疑:

void initialize(Conatiner& container) {
    MyItem item;
    container.getItem() = &item; // Wrong, item gets destroyed at the end of the function
    container.getItem() = new MyItem();          
    // This approach is filling my needs so far, but that doesn't necessarily
    // mean it's correct.
    // In particular, I'm not sure this approach properly removes the original item.
    // Here I try to use placement new to reuse the memory pointed to by container.getitem
    if (container.getItem()) {
        MyItem* pItem = container.getItem();
        pItem->~MyItem();
        pItem = new (pItem) MyItem();
    } // but if the pointer is null, I don't have any memory to reassign!
}

有没有一种习惯性的方法来处理通过指针填充字段?我无法访问 C++11 功能或 Boost 等外部库。我也不允许更改Container的界面。

initializeContainer()根本无法通过正常渠道设置Container::mItem成员。 而且不能使用 Container::getItem() 来提供该访问权限,因为它返回mItem持有的MyItem*指针,并且您无法从该指针访问mItem

您需要通过以下方式更改Container以允许访问mItem

  1. Container一个设置mItem的公共方法,然后initializeContainer()调用该方法:

    class Container {
    private:
        std::auto_ptr<MyItem> mItem;
    public:
        MyItem* getItem() { return mItem.get(); }
        void setItem(const MyItem &item) { mItem.reset(new MyItem(item)); }
    };
    void initialize(Container& container) {
        MyItem item;
        container.setItem(item);
    }
    
  2. initializeContainer()声明为Containerfriend,以便可以直接访问private成员:

    class Container {
    private:
        std::auto_ptr<MyItem> mItem;
    public:
        MyItem* getItem() { return mItem.get(); }
        friend void initialize(Container&);
    };
    void initialize(Container& container) {
        container.mItem.reset(new MyItem);
    }
    
  3. 完全摆脱initializeContainer(),改为Container一个公共初始化方法:

    class Container {
    private:
        std::auto_ptr<MyItem> mItem;
    public:
        void init() { mItem.reset(new MyItem); }
        MyItem* getItem() { return mItem.get(); }
    };
    Container c;
    c.init();
    

有没有一种习惯性的方法来处理通过指针填充字段?

不是你试图做的方式,不。 您正在尝试使用与Container对象本身无关的指针,只是由它持有。 因此,不能使用该指针访问Container对象的成员,因为它一开始并不指向Container对象。

我也不允许更改容器的接口。

好吧,那么你就不走运了,因为你试图做的事情需要改变界面。 除非您使用丑陋的指针黑客,例如:

class Container {
private:
    std::auto_ptr<MyItem> mItem;
public:
    MyItem* getItem() { return mItem.get(); }
};
void initialize(Container& container) {
    unsigned char *p = reinterpret_cast<unsigned char*>(&container);
    std::auto_ptr<MyItem> *ap = reinterpret_cast<std::auto_ptr<MyItem>*>(p + offsetof(Container, mItem));
    ap->reset(new MyItem);
}

另一方面,如果你的意图不是改变mItem本身,而只是(重新)初始化mItem已经持有的MyItem对象,你可以使用getItem(),但前提是事先创建了MyItem对象:

void initialize(Container &container) {
    MyItem *item = container.getItem();
    if (item) *item = MyItem();
}

您可以通过首先不允许mItem持有空指针来确保这一点:

class Container {
private:
    std::auto_ptr<MyItem> mItem;
public:
    Container() : mItem(new MyItem) {}
    Container(const Container &src) : mItem(new MyItem(src.getItem())) {}
    Container& operator=(const Container &rhs) { mItem.reset(new MyItem(rhs.getItem())); return *this; }
    MyItem& getItem() { return *mItem.get(); }
};

以下内容意味着对类 Container 和非空指针的修改。

例如,使用纯指针(不是auto),并通过引用返回:

#include <iostream>
class MyItem{
private:
    int a;
public:
    ~MyItem(){std::cout<<"My item destroyed!n";}
    MyItem(int _a):a(_a){};
    int getValue(){return a;}
};
class Container {
public:
    MyItem & getItem() { return *(mItem); }
private:
    MyItem* mItem;
};
void initialize(Container& container) {
    std::cout<<"New itemn";
    MyItem* p2=new MyItem(25);
    std::cout<<"Getting itemn";
    MyItem &p1 = container.getItem();
    std::cout<<"Copyingn";
    p1=*p2; // should define your own operator = for complex MyItem type
    delete p2;
    std::cout<<"New itemn";
    MyItem* p3=new MyItem(5);
    std::cout<<"Copyingn";
    p1=*p3;
    delete p3;
}
int main()
{
    Container c;
    initialize(c);
    std::cout<<c.getItem().getValue()<<std::endl;
    return 0;
}

输出:

New item
Getting item
Copying
My item destroyed!
New item
Copying
My item destroyed!
5

使用指针代替引用(仅更改相关代码块):

    ...
    MyItem* getItem() { return mItem; }
    ...
    MyItem *p1 = container.getItem();
    std::cout<<"Copyingn";
    *p1=*p2;
    ... (the same with p3)
    ...
    std::cout<<c.getItem()->getValue()<<std::endl;

下面的答案使用 C++11 .

您可以通过引用访问unique_ptr,然后使用移动语义更改智能指针指向的对象的值。

一个例子

#include <iostream>
#include <memory>
class MyItem{
private:
    int a;
public:
    ~MyItem(){std::cout<<"My item destroyed!n";}    
    MyItem(int _a):a(_a){};
    int getValue(){return a;}
};
class Container {
public:
    std::unique_ptr<MyItem> & getItem() { return mItem; }
private:
    std::unique_ptr<MyItem> mItem;
};
void initialize(Container& container) {
    std::unique_ptr<MyItem> p2(new MyItem(5));
    std::unique_ptr<MyItem> &p1 = container.getItem();
    std::cout<<"Copyingn";
    p1 = std::move(p2);
    std::unique_ptr<MyItem> p3(new MyItem(20));
    std::cout<<"Copyingn";
    p1 = std::move(p3);
}
int main()
{
    Container c;
    initialize(c);
    std::cout<<c.getItem()->getValue()<<std::endl;
    return 0;
}