2009-12-06 4 views
1

Скажем, у меня есть объект типа A, имеющий метод Initialize(). Метод получает объект B, который хранится как элемент данных объекта. Объект B разделяется между несколькими объектами, поэтому A должен содержать первоначально полученный объект B, а не его копию.Общий доступ к объекту по ссылке или указателю

class A 
    { 
     public: 
      bool Initialize(B?? b); 
     private: 
      B?? m_B;  
    } 

Объект B должен быть представлен. Таким образом, я думаю, передать его по ссылке, вместо того, чтобы передавать указатель и не инициализировать() в случае, когда B является NULL.

Единственная альтернатива для m_B должна быть типа указателя B (он не может быть ссылкой на B, так как инициализация B не выполняется в A c-tor). Таким образом, Инициализация должна выглядеть так:

bool A::Initialize(B& b) 
{ 
    m_B = &b; 
    .... 
} 

Это aproach в порядке?

UPD:

  1. код не является новой, и я просто пытаюсь "исправить" некоторые проблемы. На самом деле я не говорю о некоторых конкретных классах A и b, скорее о том, как проблема приближается к моей базе кода. Код широко передает указатель на B и проверяет его в Initialize(), если он NULL.

  2. Передача B в c-tor не всегда является хорошим вариантом. Также есть другие параметры, переданные в A, которые не существуют во время создания A. Поэтому я не хотел бы передавать часть параметров A-c-tor, а остальное - в A :: Initialize().

  3. shared_ptr тоже может быть «NULL», поэтому передача его в A :: Initialize() не отличается от передачи только указателя на B, в том аспекте, что подпись Initialize() не объявляет, если B является обязательным или не. В моем случае это так, и я хочу выразить это, передав ссылку на B.

  4. В настоящее время наш код не использует boost. Итак, хотя решение shared_ptr лучше, чем просто передать необработанный указатель, может ли предложенное мной решение рассматриваться как плохое, но все же решение.

+2

У вас не должно быть функции Initialize() - у вас должен быть конструктор. – 2009-12-06 17:38:58

+0

Вы можете вытащить отдельные компоненты из бустера с помощью bcp: http://www.boost.org/doc/libs/1_41_0/tools/bcp/bcp.html –

ответ

1

Я бы остался с указателем. Ссылка здесь просто отправляет неправильное сообщение.

Вы не можете использовать ссылки на объект в ситуациях, когда вы планируете принимать указатель на объект и сохранить или поделиться им, и т.д. Основной причиной ссылок в C++ это позволяют таким вещи, как оператор перегрузке и скопировать конструкторы работы для пользовательских типов. Без них было бы сложно обеспечить эту функциональность синтаксисом , который не отличается от встроенных типов.

Но в этой ситуации вы не пытаетесь имитировать встроенный тип. Вы работаете на объекте, который обычно используется с помощью указателя, и даже используется несколькими различными указателями. Итак, не стесняйтесь сообщить об этом.

Что касается b, являющегося NULL, обязательно используйте assert(b) (или аналогичную конструкцию) для принудительного исполнения контракта и прекращения недействительной программы. (я бы не выбрал исключение. Даже если вы забудете о проблемах с исключениями в C++, , вы планируете когда-либо поймать и обработать такое исключение в своем коде?) Я бы также добавил такие утверждения ко всему коду, который использует m_B в случае, если кто-то забыл позвонить A::Initialize().

Использование ссылки для обеспечения того, чтобы указатель не был нулевым, может пропустить пропуски. В большинстве реализаций вы можете сделать ссылку от NULL или оборванного указателя без каких-либо ошибок. Ваше приложение будет работать только в том случае, если вы попытаетесь использовать эту ссылку. Так что если кто-то случайно передает вам B *pb, что равно NULL, , вы можете позвонить pa->Initialize(*pb) и ничего не происходит. За исключением того, что pa->m_B теперь NULL.

Следует ли использовать что-то вроде boost::shared_ptr зависит от вас и вашей стратегии памяти Управление. Это совершенно не связанная проблема.

+1

Извините, я полностью не согласен. Передача указателя говорит: «Это может быть необязательно». Предположение о том, что вы утверждаете, что это null, означает, что вам нужно документировать факт, что функция, хотя и принимает указатель, должна принимать указатель, который не может быть нулевым. Принятие ссылки говорит: «Это необязательно»; ваш аргумент о том, что вы не используете ссылку, потому что она может быть нулевой в любом случае, IMHO, скорее всего, была проблемой для вас просто потому, что ваши проекты нарушены, потому что вы берете указатель, где вы должны взять ссылку, и вы не применяете необязательный или требуемый характер ваших параметров. ;) –

+0

Расслабьтесь, иногда люди не согласны с вами. –

+0

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

0

Я бы пошел с boost :: shared_ptr. Если вы используете ссылки, вам может потребоваться беспокоиться об объеме референта, если вы используете простой указатель, вы попадаете в проблемы с управлением памятью. Кто собирается удалить B?

Возможно, просто подумайте о своей схеме еще раз: вам действительно нужно разделить объект или копию ctor для типа B достаточно? Если B будет изменен другими классами, и эти изменения должны быть известны другим классам, вам понадобится shared_ptr.

+2

Как вы знаете, что это динамически распределено? – 2009-12-06 17:43:48

2

Если B не является обязательным, его можно представить в виде указателя. Если требуется B, то он должен быть представлен как ссылка.

Если возможно, попробуйте избежать «двухступенчатой ​​конструкции» с помощью метода инициализации. Если это невозможно сделать, а затем внутри A вам нужно рассматривать B как необязательный и хранить его как указатель и тест, где бы вы ни захотели его использовать.

Если ваш метод инициализации (или, в идеале, конструктор) требует B, тогда вы должны передать его в качестве ссылки.

Все это предполагает, что вы знаете, кто на самом деле владеет B; возможно, B владеет экземплярами A и инициализирует их ссылками на себя или, возможно, B принадлежит тому, что также принадлежит всем экземплярам A, которые относятся к этому экземпляру B.

Если объекты A собственного B совместно то вы должны использовать что-то вроде boost :: shared_ptr, чтобы сделать совместное владение явным; предполагая, что B динамически выделяется новым.

+0

Я не согласен. Вы сбиваете с толку, как вы связываете объекты с их жизнью. Оба должны быть явными, и оба они не должны быть связаны. Говорить, что это должен быть указатель, потому что это указатель где-то, и он может стать недействительным, является ложным; все может быть представлено адресом (указателем), и все может стать недействительным, если вы не справитесь с его временем жизни. В этой ситуации код получает ссылку на объект, который должен существовать; поэтому используйте ссылку. Внутри кода из-за двухэтапной инициализации объект является необязательным, поэтому используйте указатель, который может быть либо действительным, либо нулевым. –

+0

Хм, и теперь, когда предыдущий комментарий был удален, кажется, я разговариваю сам с собой ... –

+0

Простите об этом. Я просто почувствовал, что тон моих комментариев стал несколько грубым и удалил их. Может быть, слишком быстро. –

0

Я считаю, что вы должны использовать конструктор вместо использования метода Initialize.

Передача B в c-tor не всегда является хорошим вариантом. Есть также другие параметры , переданные в A, которые не являются , существует в момент времени создания. Поэтому I woudln't предпочитает передавать часть параметров в c-tor, а остальное - A :: Initialize().

Вы слышали о предконструкции?

Вот пример того, что вы могли бы сделать из вместо:

class IsEmpty{}; 
class A 
{ 
    B *b_; 
    int *c_; 
    char *d_; 

    void Initialize(B *b) 
    { 
     b_ = b; 
     c_ = b_->Getc(); 
     d_ = b_->Getd(); 
    } 

public: 
    A(B *b) 
    : b_(0), c_(0), d_(0) 
    { 
     if(b == 0) throw IsEmpty(); 
     Initialize(b); 
    } 
}; 
1

Передача В качестве справки говорит срок службы B больше, чем время жизни (или время деинициализация) А. Если это случай, вы должны пройти переизбранием. Внутренне вы также можете сохранить ссылку в Boost reference wrapper (но это Boost, поэтому, возможно, не вариант).

Если вы передадите указатели, и вы уверены, что они никогда не должны быть NULL, если ваша программа верна, то используйте assertions скорее, чем if-clause, чтобы проверить это.

У меня тоже иногда есть szenario you desrcibe и обычно используют ваше предлагаемое решение. Это самый простой.

В зависимости от сложности и дизайна ваших классов вы также можете использовать вариант state pattern, где объект-состояние, описывающий «инициализированный» -стат, строится в методе initialize. В этом случае вы можете передать ссылку на конструктор объекта state. Вероятно, это излишний (модель состояния на C++ имеет значительную плиту котла, поэтому убедитесь, что она того стоит) и требует много рефакторинга, но, может быть, это поможет вам каким-то образом.

0

Я бы определенно избегал использования ссылки в качестве члена класса, если это вообще возможно. Есть миллион способов, которыми это может вас повредить. Если вы это сделаете, вы должны использовать список инициализаторов, но это еще одна функция, которую вам следует избегать, как чума. Он работает отлично для простых случаев, но это может вызвать серьезные проблемы.

Смежные вопросы