Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Общие вопросы C/C++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 31.07.2013, 11:19   #1
sVasilich
Форумчанин
 
Аватар для sVasilich
 
Регистрация: 16.12.2009
Сообщений: 224
По умолчанию weak_ptr и this

Допустим, есть следующий код:

Код:
class B;

class A {
public:
    A();
    ~A();

private:

    B* ptrB;

};

class B {

public:
    B(A* _a);
    ~B();

private:
    A* ptrA;
};

A::A() {
    ptrB = new B(this);
}

A::~A() {
    delete ptrB;
}

B::B(A *_a) {
    ptrA = _a;
}

B::~B() {
    ptrA = NULL;
}
Задача: заменить обычные указатели на интеллектуальные. В классе A используется std::shared_ptr, в классе B - std::weak_ptr.

Как правильно реализовать конструктор класса B?

Код:
class B;

class A {
public:
    A();
    ~A();

private:
    std::shared_ptr<B> ptrB;

};

class B {

public:
    B(/*????*/);
    ~B();

private:
    std::weak_ptr<A> ptrA;
};

A::A() {
    ptrB = std::shared_ptr<B>(new B(/*???*/));
}

A::~A() {
}

B::B(/*????*/) {
    //????
}

B::~B() {
}
Люди бывают 10 типов: те, кто понимают двоичную систему счисления, и те, кто не понимают...
sVasilich вне форума Ответить с цитированием
Старый 31.07.2013, 18:17   #2
pproger
C++ hater
СтарожилДжуниор
 
Аватар для pproger
 
Регистрация: 19.07.2009
Сообщений: 3,333
По умолчанию

2sVasilich
если ты собрался в классах использовать умные указатели, ссылающиеся друг на друга, тебе сначала нужно где-то создать объекты этих классов.
можно сделать например так:
Код:
#include <iostream>

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>

using boost::shared_ptr;
using boost::weak_ptr;
using namespace std;

class B;

class A {
public:
	~A() {
		cout << "A::~A" << endl;
	}

	void setB(const shared_ptr<B> &b) {
		ptrB = b;
	}

private:
	shared_ptr<B> ptrB;
};

class B {
public:
	~B() {
		cout << "B::~B" << endl;
	}

	void setA(const shared_ptr<A> &a) {
		ptrA = a;
	}

private:
	weak_ptr<A> ptrA;
};

int main()
{
	shared_ptr<A> a(new A);
	shared_ptr<B> b(new B);

	a->setB(b);
	b->setA(a);

	return 0;
}
I invented the term Object-Oriented, and I can tell you I did not have C++ in mind. (c)Alan Kay

My other car is cdr.

Q: Whats the object-oriented way to become wealthy?
A: Inheritance
pproger вне форума Ответить с цитированием
Старый 01.08.2013, 15:27   #3
sVasilich
Форумчанин
 
Аватар для sVasilich
 
Регистрация: 16.12.2009
Сообщений: 224
По умолчанию

pproger, спасибо за ответ.

Получается, что задачу (вроде как тривиальную), приведенную в первом фрагменте кода с помощью интеллектуальных указателей решить нельзя?

Т.е. есть родительский объект, у него есть дочерний объект. Родительский объект об этом знает. Дочерний объект знает о своём родителе. Вне родителя дочерний объект не виден.

При этом, сырые указатели использовать не хочется, чтобы ребёнок не мог сделать, например, так:

Код:
void B::crash() {
    delete ptrA;
}
Как правильно поступить в таком случае?
Люди бывают 10 типов: те, кто понимают двоичную систему счисления, и те, кто не понимают...

Последний раз редактировалось sVasilich; 01.08.2013 в 15:31.
sVasilich вне форума Ответить с цитированием
Старый 01.08.2013, 15:51   #4
Rififi
Старожил
 
Регистрация: 19.08.2009
Сообщений: 2,119
По умолчанию

sVasilich

Задача: заменить обычные указатели на интеллектуальные. В классе A используется std::shared_ptr, в классе B - std::weak_ptr.

Код:
struct Child;

struct Parent : public boost::enable_shared_from_this<Parent>
{
	void CreateChild();
	boost::shared_ptr<Child> child_;
};

struct Child
{
	Child(boost::shared_ptr<Parent> parent) : parent_(parent) {}
	boost::weak_ptr<Parent> parent_;
};

void Parent::CreateChild()
{
	child_.reset(new Child(shared_from_this()));
}

boost::shared_ptr<Parent> parent(new Parent());
parent->CreateChild();
Rififi вне форума Ответить с цитированием
Старый 01.08.2013, 15:59   #5
Rififi
Старожил
 
Регистрация: 19.08.2009
Сообщений: 2,119
По умолчанию

sVasilich

При этом, сырые указатели использовать не хочется, чтобы ребёнок не мог сделать, например, так:

Пытаться забить гвозди микроскопом - это дело конечно очень весёлое...
Но нормальные люди добиваются той же задачи делая деструктор закрытым :lol:
Rififi вне форума Ответить с цитированием
Старый 01.08.2013, 16:19   #6
sVasilich
Форумчанин
 
Аватар для sVasilich
 
Регистрация: 16.12.2009
Сообщений: 224
По умолчанию

Rififi, спасибо, то что нужно!
Про enable_shared_from_this я нагуглил, но с использованием не разобрался, теперь понятнее.

Но нормальные люди добиваются той же задачи делая деструктор закрытым :lol:
А можно про правильный способ забивания гвоздей на примере? Как потом вызывается деструктор, когда нужно действительно удалить родителя?
Люди бывают 10 типов: те, кто понимают двоичную систему счисления, и те, кто не понимают...
sVasilich вне форума Ответить с цитированием
Старый 01.08.2013, 23:35   #7
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Цитата:
Сообщение от sVasilich Посмотреть сообщение

Но нормальные люди добиваются той же задачи делая деструктор закрытым :lol:
А можно про правильный способ забивания гвоздей на примере? Как потом вызывается деструктор, когда нужно действительно удалить родителя?
Хотел сначала спросить: на кой нужен слабый указатель (мне на практике ни разу не пригодился, поэтому любопытно) .

Но почитав тред, понял: в конкретно данной ситуации его существование излишне.

Ребенок не самостоятелен. Он - собственность родителя. Нет родителя - нет детей.

Родитель держит сильный указатель на детей. Дети держат сырой указатель на родителя.

Детенка не может существовать без родителя. Следовательно, сырой указатель на родителя априори валидный.
Поэтому, пока детенка жив - он всегда сможет безопасно обратится к родителю.
Мертвые детенки уже ни к кому не обращаются, потому что их больше не существует.

При разрушении Родителя, сильные указатели автоматически ликвидируют детей.

Вместо сильного можно использовать unique_ptr.

Хотя лично я предпочитаю иметь дело с как можно меньшим количеством сущностей в коде.

На моей практике shared_ptr покрывает всю область применения указателей.

Ни разу не сталкивался с ситуацией, когда кроме него потребовался бы ещё какой нибудь.


Что касается закрытых диструкторов: детенка объявляет родителя другом. Теперь только родитель сможет ликвидировать дитенку. Больше никто.

Но в этом нет никакого смысла, ибо кроме родителя все равно никто к детенке доступа не имеет.

Можно закрыть диструктор родителя, что бы его "ничайно" не прибил детенка. Однако, делать защиту от саботажа (ну или от дибилов) - напрасное усложнение кода и трата времени.

На верхнем (уровень бизнес-логики) уровне самостоятельное использование new/delete - признак говнокода.

Последний раз редактировалось _Bers; 01.08.2013 в 23:42.
_Bers вне форума Ответить с цитированием
Старый 01.08.2013, 23:57   #8
sVasilich
Форумчанин
 
Аватар для sVasilich
 
Регистрация: 16.12.2009
Сообщений: 224
По умолчанию

Цитата:
Хотел сначала спросить: на кой нужен слабый указатель (мне на практике ни разу не пригодился,
По идее, в этой ситуации (пост 2):

Код:
	shared_ptr<A> a(new A);
	shared_ptr<B> b(new B);

	a->setB(b);
	b->setA(a);
если в A и B будут shared_ptr, получится циклическая зависимость и память освобождена не будет.. я так понял.

Цитата:
Можно закрыть диструктор родителя, что бы его "ничайно" не прибил детенка.
Вот здесь интересно, кто будет вызывать деструктор родителя? Нужно будет делать дружетсвенный shared_ptr<Parent>, я правильно понял? Другого варианта пока не вижу...

Цитата:
Родитель держит сильный указатель на детей. Дети держат сырой указатель на родителя.
Т.е. в таком контексте использовать сырые указатели хорошо и правильно, ответственность за него ложится полностью на клиента? Как я понял, weak_ptr как раз и есть защита от саботажа и дебилов.

Цитата:
На верхнем (уровень бизнес-логики) уровне самостоятельное использование new/delete - признак говнокода.
Другими словами:
Код:
Parent* pt = new Parent() //плохо

...

Parent() {
  mChild = new Child(); //вполне допустимо
}
?

ps
Положительный отзыв оставить система не даёт вообще ни кому из ответивших, поэтому придётся ограничится устным спасибо
Люди бывают 10 типов: те, кто понимают двоичную систему счисления, и те, кто не понимают...
sVasilich вне форума Ответить с цитированием
Старый 02.08.2013, 01:43   #9
_Bers
Старожил
 
Регистрация: 16.12.2011
Сообщений: 2,329
По умолчанию

Цитата:
Сообщение от sVasilich Посмотреть сообщение
если в A и B будут shared_ptr, получится циклическая зависимость и память освобождена не будет.. я так понял.
В данном случае умный указатель нужен только родителю на детей.

А вообще, я ни разу за все время не сталкивался с циклическими ссылками. В том плане, что ни разу не было необходимости в архитектуре "ресурсы, которые владеют друг другом".

Всегда был только один хозяин, который владел ресурсами. Но никогда ресурсы не владели своим хозяином.

В чужом коде встречал, да. Но там налицо было переусложнение кода.
Так, словно архитектура была создана ради архитектуры, а не для решения задач насущных.


Цитата:
Сообщение от sVasilich Посмотреть сообщение
Вот здесь интересно, кто будет вызывать деструктор родителя?
Я не телепат. Откуда я знаю, кто владеет вашим родителем.

Цитата:
Сообщение от sVasilich Посмотреть сообщение
Нужно будет делать дружетсвенный shared_ptr<Parent>, я правильно понял? Другого варианта пока не вижу...
Не понял момента. Зачем делать дружественный shared_ptr<Parent>?

Цитата:
Сообщение от sVasilich Посмотреть сообщение
Т.е. в таком контексте использовать сырые указатели хорошо и правильно, ответственность за него ложится полностью на клиента? Как я понял, weak_ptr как раз и есть защита от саботажа и дебилов.
Клиенты, то бишь те самые детишки, которые держат сырой указатель, используют его только и только для доступа к объекту.
Ничего другого они с ним больше не делают.

Достаточно взять себе за правило: не удалять, и не создавать самостоятельно ничего по сырому, и все будет оккей.

Но и без этого во всех книжках пишут: кто выделял, тот и должен удалять, или получится каша.

Делать защиту от дибилов - глупо.
От саботажа - ещё глупее.

Цитата:
Сообщение от sVasilich Посмотреть сообщение
Другими словами:
Код:
Parent* pt = new Parent() //плохо
...
Parent() {
  mChild = new Child(); //вполне допустимо
}
?
Нет. Оби ситуации плохи. Потому что в обеих используется new.

Когда я пишу: "не нужно использовать низкоуровневую работу с выделением/освобождением памяти на уровне бизнес-логики приложения", то я имею ввиду вообще не использовать операторы new/delete

Если нужно создавать объекты в куче, к вашим услугам контейнера, которые сделают это все за вас:

Код:
std::vector<Some> vec;
vec.push_back( Some(param) );
...

shared_ptr<Some> smart1 = make_shared<Some>(param);
...
shared_ptr<ISome> smart2 = make_shared<Some>(param);
Контейнера сами проконтролируют вопросы безопасности.
Сырые же указатели лучше использовать только там, где архитектура гарантирует валидность сырого указателя.

Низкоуровневую же материю стоит трогать только если задача - написать собственный инструментальный механизм, наподобии пула памяти, или интеллектуального указателя.

В вашем случае ребенку можно предоставить сырой указатель.
Слабый указатель в этой ситуации избыточен.

Последний раз редактировалось _Bers; 02.08.2013 в 01:50.
_Bers вне форума Ответить с цитированием
Старый 02.08.2013, 09:42   #10
sVasilich
Форумчанин
 
Аватар для sVasilich
 
Регистрация: 16.12.2009
Сообщений: 224
По умолчанию

_Bers, спасибо, теперь всё стало на свои места.

Цитата:
Я не телепат. Откуда я знаю, кто владеет вашим родителем.
Цитата:
Не понял момента. Зачем делать дружественный shared_ptr<Parent>?
Неправильно выразился. Я хотел сказать, что владелец родителя должен быть дружественным по отношении к нему. Если создаём Parent в main(), то непонятно кто его будет удалять. Поэтому и пришла в голову обёртка в виде дружественного shared_ptr<Parent>. В общем, по этому вопросу мне всё понятно.

Кхм.. Последний кусок кода, это мне уже видно спать хотелось Конечно же должно быть:

Код:
Parent() {
   mChild = std::sgared_ptr<Child>(new Child(this));
}

Child(Parent* _parent) {
  parentPtr = _parent;
}
Ещё раз спасибо всем, кто принял участие в теме.
Люди бывают 10 типов: те, кто понимают двоичную систему счисления, и те, кто не понимают...
sVasilich вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц