Como lidar com ponteiros de criança para pai?

5

Eu tenho uma classe que representa um arquivo em formato binário específico no disco (o pai no título) e outra classe que representa um objeto dentro desse arquivo (filho). Quando o objeto muda, às vezes ele precisa ser realocado para uma nova posição no arquivo e a classe de arquivo precisa ser notificada sobre essa nova posição.

Para fazer isso, adicionei um campo weak_ptr ao objeto, que aponta para o arquivo. Exceto que isso significa que tem que haver um shared_ptr para o arquivo, então estou usando um método static factory em vez de construtor público para ele. E o objeto agora não pode ser inicializado no construtor, então estou usando unique_ptr para ele.

No código, parece algo assim:

class File;

class Object
{
private:
    std::weak_ptr<File> file;

public:
    Object(std::weak_ptr<File> file)
        : file(file)
    {}

    void modify();
};

class File
{
private:
    std::unique_ptr<Object> object;
    std::shared_ptr<File> self;

    File()
    {}

public:
    void setObjectPosition(std::uint64_t newPosition);

    static std::shared_ptr<File> create();
};

void Object::modify()
{
    std::uint64_t newPosition = …;

    file.lock()->setObjectPosition(newPosition);
}

void File::setObjectPosition(std::uint64_t objectPosition)
{
    // store objectPosition
}

std::shared_ptr<File> File::create()
{
    auto file = std::shared_ptr<File>(new File());

    file->self = file;
    file->object = std::unique_ptr<Object>(new Object(file));

    return file;
}

Esta é a abordagem correta para fazer isso? Ou existe uma solução melhor / mais idiomática? Eu sinto que estou usando *_ptr demais, mas não consigo pensar em nada melhor.

    
por svick 12.09.2013 / 20:31
fonte

2 respostas

5

Se tudo que você precisa é de um ponteiro não próprio para um objeto, basta usar um ponteiro bruto. Aqui está um exemplo menor:

template <typename T> struct binary_tree_node
{
    std::unique_ptr<binary_tree_node> left_child_;
    std::unique_ptr<binary_tree_node> right_child_;
    binary_tree_node* parent_ = nullptr;
    T data_; 
};

shared_ptr e unique_ptr implicam algum tipo de propriedade, que a criança - > pai não tem, desde que o pai seja o proprietário da criança. Portanto, um ponteiro bruto está totalmente correto nesta situação (apenas não delete it!)

    
por 12.09.2013 / 22:55
fonte
0

Nesses casos, acho melhor usar um ponteiro nu da criança, se você não puder garantir que o pai / mãe sai do filho, deixe o destrutor configurá-lo como 0 para indicar que a criança não tem pai. Desta forma, você não precisa da construção em 2 fases, e você não paga a sobrecarga de weak_ptr<>::lock em cada turno.

Você provavelmente também deve dar uma olhada em boost::enable_shared_from_this<> , na verdade, com a abordagem que você está usando agora com o File mantendo uma referência explícita a si mesmo, você derrota o propósito de usá-lo em primeiro lugar.

Além disso, geralmente é uma boa idéia usar boost::make_shared<> , já que ele usa menos alocações (economizando memória e ciclos) e tem melhor comportamento de cache por toda parte (já que você costuma cutucar a contagem de referências quando toca o objeto).

    
por 24.09.2013 / 17:22
fonte