Конструкторы, принимающие строковые ссылки. Плохая идея? - PullRequest
2 голосов
/ 19 декабря 2009

Это считается плохой идеей / плохим дизайном, есть класс с конструктором, принимающим ссылку, как показано ниже?

class Compiler
{
public:
  Compiler( const std::string& fileName );
  ~Compiler();
//etc
private: 
  const std::string& m_CurrentFileName;
};

или я должен использовать значения? Я действительно забочусь о производительности.

Ответы [ 8 ]

16 голосов
/ 19 декабря 2009

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

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

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

3 голосов
/ 19 декабря 2009

Это нормально, если конструктор просто использует его, не сохраняя его, копирует его для дальнейшего использования (в этот момент использование ссылки, вероятно, не имеет значения) или принимает на себя владение им (что сомнительно, потому что вы ' зависит от пользователя, чтобы он вел себя правильно и не использовал строковую ссылку в дальнейшем).

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

2 голосов
/ 19 декабря 2009

Я согласен с другими людьми, что вам следует больше беспокоиться о корректности и надежности производительности (поэтому переменная-член должна быть копией, а не ссылкой) и что, если вы действительно беспокоитесь о производительности, вам следует профилировать свой код .

Тем не менее, не всегда ясно, что передача по константной ссылке происходит быстрее. Например, если вместо этого вы передаете значение по значению, а аргумент является r-значением, компилятор может выполнить elision копии (см. http://cpp -next.com / archive / 2009/08 / want-speed-pass -by-value / ) и избегать дополнительной копии при сохранении ее в переменную-член. (Это не очень интуитивно понятно, и, вероятно, вы не захотите делать это повсюду, поэтому еще раз: профиль!)

2 голосов
/ 19 декабря 2009

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

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

2 голосов
/ 19 декабря 2009

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

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

1 голос
/ 20 декабря 2009

Ваш класс должен быть автономным и избегать ненужных зависимостей. В вашем примере ваш класс "Compiler" будет зависеть от строки CurrentFileName в течение всего ее существования. Это означает, что если CurrentFileName будет уничтожено до компиляции, у вас возникнет проблема.

Зависимые лица и менеджеры

Итак, я полагаю, это зависит от характера зависимости между классом Dependent и классом it Manager (т.е. класс Dependent зависит от класса Manager, или, в вашем примере, класс Compiler зависит от std :: string класс) ...

  • Если зависимость «мягкая», то Dependent должен сделать копию класса Manager.
  • Если зависимость "сильная", то у зависимой может быть ссылка на класс Manager.

    Мягкий против сильного

Пример "мягкой зависимости" - это когда вашему Зависимому требуется только значение, а не сам объект. Строка обычно рассматривается как значение.

Пример "сильной зависимости" - это когда вашему Зависимому нужен доступ к своему Менеджеру, или когда у Зависимого нет смысла без Менеджера (то есть, если Менеджер уничтожен, тогда все Зависимые должны быть уничтожены раньше) 1017 *

Заключение

Обычно зависимость мягкая.

Если сомневаешься, считай это мягким. У вас будет меньше проблем (это один из способов получить симпатичную ошибку сегмента C ++ без арифметики с указателями), и у вас все еще будет возможность оптимизировать его , если необходимо .

о вашем случае: Soft

Сделайте себе одолжение и сделайте копию стоимости. Оптимизация - корень всего зла , и, если ваш профилировщик не скажет, что копия строки является проблемой, сделайте копию, как показано ниже:

class Compiler
{
public:
  Compiler( const std::string& fileName );  // a reference
  ~Compiler();
//etc
private: 
  const std::string m_CurrentFileName;      // a value
};

Compiler::Compiler(const std::string& fileName )
   : m_CurrentFileName(fileName)            // copy of the filename
{
}

О моем случае: Сильный

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

В настоящее время я работаю над кодом, где пользователь создает объекты Notifier для подписки на события и извлекает их при необходимости. Объект Notifier прикреплен к Manager при создании и не может быть отсоединен от него.

Выбор дизайна заключается в том, чтобы навязывать пользователю библиотеки менеджер изживает Уведомитель. Это означает следующий код:

Manager manager ;
Notifier notifier(manager) ;  // manager is passed as reference

Код Notifier очень похож на код, который вы предложили:

class Notifier
{
   public :
      Notifier(Manager & manager) : m_manager(manager) {}
   private :
      Manager & m_manager ;
} ;

Если вы внимательно посмотрите на дизайн, m_manager используется как неизменный указатель на объект Manager. Я использую ссылку C ++, чтобы быть уверенным:

  1. Ссылка определяется при создании и никогда не будет изменена
  2. Ссылка никогда не должна быть NULL или недействительной

Это часть контракта.

1 голос
/ 19 декабря 2009

Если вы пишете компилятор, копирование имени файла один или два раза не станет узким местом. Это в большей степени проблема стиля C ++, которую я оставляю более опытным людям на C ++.

0 голосов
/ 19 декабря 2009

Если вы сильно обеспокоены производительностью, то лучше всего перейти по ссылке.

Подумайте над следующим примером, чтобы сделать картинку более четкой:

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

class B : public A{
  public : B() {}
};
class MyClass{

 B bObj; 
public: 
 MyClass(B b) : bObj(b) { } // constructor and destructor overhead
 MyClass(B &b) { }

};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...