shared_ptr i wkaźnik this

0

Mam problem z poniższym kodem:

#include "stdafx.h"
#include <memory>
#include <vector>
#include <string>
#include <iostream>

class Block;

class Document
{
public:
	Document() {}
	~Document() {}
private:
	std::vector<std::shared_ptr<Block> > item;
public:
	void addBlock(std::shared_ptr<Block> block)
	{
		item.push_back(block);
	}
	void showBlock(void) const
	{
		/*for (std::shared_ptr<Block> i : item)
		{
			i->showText();
		}*/
	}
};

class Block : public std::enable_shared_from_this<Block>
{
public:
	Block(Document& doc, std::string text)
	{
		doc.addBlock(shared_from_this());
		this->text = text;
	}
	~Block() {}
public:
	void showText() const
	{
		std::cout << text << std::endl;
	}
private:
	std::string text;
};


int _tmain(int argc, _TCHAR* argv[])
{
	Document docObj;
	std::shared_ptr<Block> block1(new Block(docObj, "napis1"));
	std::shared_ptr<Block> block2(new Block(docObj, "napis2"));

	docObj.showBlock();
	
	return 0;
}

Wywala błąd std::bad_weak_ptr at memory location Za bardzo nie rozumiem, jak powinienem wykorzystać weak_ptr i jak to powiązać z shared_ptr.

3

shared_from_this możesz użyć dopiero wtedy, kiedy zostanie stworzona co najmniej jedna strong reference na obiekt. Co znaczy, że nie możesz tego użyć przed wykonaniem się któregoś z wszystkich jego konstruktorów.

7

Pingwin jest blisko, ale nie w pełni.

shared_from_this możesz użyć dopiero po utworzeniu instancji shared_ptr trzymającej tworzony obiekt, a więc po wykonaniu się wszystkich jego konstruktorów w danym wywołaniu - bo dopiero wtedy shared_ptr ma szansę się zarejestrować.

0

Kurcze, logiczne. To może z innej beczki zapytam, bo właśnie moja koncepcja legła w gruzach. Jak mógłbym automatycznie przekazać wskaźnik do instancji klasy w momencie jej utworzenia? Chodzi o to, że nie chcę obiektów klasy Block tworzyć bez przypisania do obiektu Document.

0

Może coś w rodzaju make_shared?

shared_ptr<Block> Document::addBlock(std::string text) 
0
vpiotr napisał(a):

Może coś w rodzaju make_shared?

shared_ptr<Block> Document::addBlock(std::string text) 

Myslałem o tym. Tylko, że docelowo będę miał różne typy Bloków: TextBlock, ImageBlock, TableBlock itd, które będą dziedziczyć po klasie abstarkcyjnej Block. Musiałbym dodawać kolejne funkcje addTextBlock, addImageBlock... i wydaje mi się to kiepski pomysł ze względu na skalowalność przy dodawaniu w przyszłości ewentualnych innych bloków

0

A dziedziczenie? Block bazowa. TextBlock, ImageBlock, TableBlock pochodnymi.
Całość trzymać jako smart pointer klasy Block w jakimś kontenerze, np. std::vector.

1

Moim zdaniem to doc.addBlock(shared_from_this()); jest bardzo nieładne i niejasne rozwiązanie. Prawdopodobnie dużo lepsze byłoby coś takiego:

int _tmain(int argc, _TCHAR* argv[])
{
    Document docObj;
    std::shared_ptr<Block> block1(new Block("napis1"));
    std::shared_ptr<Block> block2(new Block("napis2"));
    doc.addBlock(block1);
    doc.addBlock(block2);
    docObj.showBlock();
 
    return 0;
}
4

Prawidłowym rozwiązaniem jest coś co się nazywa "two step initialization".
Generalnie chodzi o to, że proces konstrukcji ma dwie fazy:

  • podstawowa inicjalizacji jest wykonywana w konstruktorze
  • dodatkowa inicjalizacja wykonywana na późniejszym etapie z metody fabrykującej
class Block : public std::enable_shared_from_this<Block>
{
private:
    Block(const std::string &text)
    {
        this->text = text;
    }

    void setup(Document& doc) {
        doc.addBlock(shared_from_this());
    }

public:
    static std::shared_ptr<Block> create(Document& doc, const std::string &text) {
         auto result = std::make_shared<Block>(text);
         result->setup(doc);

         return result;
    }
    ~Block() {}
public:
    void showText() const
    {
        std::cout << text << std::endl;
    }
private:
    std::string text;
};

Osobiście nie lubię tego rozwiązania, bo wskazuje że coś jest nie tak z odpowiedzialnością klasy.
IMO Block nie powinien modyfikować dokumentu w ten sposób. Masz cykl zależności między klasami (Block zależy od Document i vice versa), a to jest zawsze prowadzi to kłopotów.

1 użytkowników online, w tym zalogowanych: 0, gości: 1