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

时间:2016-07-07 16:09:50

标签: c++ pointers c++98

我有一个容器类:

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

的界面

2 个答案:

答案 0 :(得分:2)

initializeContainer()无法通过普通渠道设置Container::mItem成员。并且您无法使用Container::getItem()来提供该访问权限,因为它会返回MyItem*所持有的mItem指针,并且您无法从该指针到达mItem。< / p>

您需要更改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()声明为friend Container,以便它可以直接访问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();
    
  4.   

    是否有通过指针处理填充字段的惯用方法?

    不是你试图这样做的方式,不。您正在尝试使用与Container对象本身无关的指针。因此,您无法使用该指针访问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本身,而只是(重新)初始化MyItem已经拥有的mItem对象,那么可以使用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(); }
    };
    

答案 1 :(得分:1)

以下意味着修改类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 item\n";
    MyItem* p2=new MyItem(25);
    std::cout<<"Getting item\n";
    MyItem &p1 = container.getItem();
    std::cout<<"Copying\n";
    p1=*p2; // should define your own operator = for complex MyItem type
    delete p2;
    std::cout<<"New item\n";
    MyItem* p3=new MyItem(5);
    std::cout<<"Copying\n";
    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<<"Copying\n";
    *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<<"Copying\n";
    p1 = std::move(p2);
    std::unique_ptr<MyItem> p3(new MyItem(20));
    std::cout<<"Copying\n";
    p1 = std::move(p3);
}

int main()
{
    Container c;
    initialize(c);
    std::cout<<c.getItem()->getValue()<<std::endl;
    return 0;
}