Как правильно использовать const в C ++? - PullRequest
5 голосов
/ 15 октября 2010

const правильность меня несколько смущает.

Какое эмпирическое правило вы используете, чтобы решить, когда что-то должно быть постоянным или нет?

например. рассмотрим этот пример

class MyClass
{
  string ToString(); // this one?
  const string& ToString(); // or this? 
  const string& ToString() const; // or this?

  char* ToString(); // What about this?
  const char* ToString(); // or this?
  const char* ToString() const; // or this?
  const char const* ToString(); // Is this legal?
  const char const* ToString() const; // how about this?
  char const* ToString();  // or even this?
};

Const может запутать.

Какая разница между всеми этими методами ToString?

Если я правильно понимаю, первый возвращает новый строковый объект, который можно изменить если нужно. Второй возвращает постоянную ссылку, возможно, это должна быть строка const & ToString (). Третий, вероятно, бессмыслица, потому что ссылки всегда постоянны, верно?

Думал, что я добавлю туда старые версии char * для сравнения, поскольку у меня есть методы, которые возвращают указатели на объекты, и я не уверен, должны ли они быть константными или нет.

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

РЕДАКТИРОВАТЬ: также, как мне справиться с этим «... отбрасывает квалификаторы». Что это на самом деле означает?

Ответы [ 6 ]

2 голосов
/ 15 октября 2010

Где вы используете const зависит от назначения функции. Как предлагает Джеймс в своем комментарии (который стоит поставить в качестве ответа), поставьте const везде, где вы можете:

Если функция предназначена для изменения состояния в своем экземпляре объекта, не ставит const в конце подписи.

Если функция предназначена для изменения одного из параметров ссылки или указателя, не ставьте const в параметре.

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

Если возвращенная ссылка / указатель ссылается на переменные, которые не должны изменяться полученным, do ставит const на тип.

Отвечать на примеры, приведенные в вопросе, невозможно без знания цели функций. Моя тенденция заключается в использовании string ToString() const и char* ToString() const, с очень четкой документацией о том, кто отвечает за delete в char*.


В качестве дополнительной заметки const char* и char const* идентичны (указатель на неизменяемые символы). char* const, с другой стороны, является неизменяемым указателем на изменяемые символы.

1 голос
/ 15 октября 2010

Я всегда использовал этот FAQ для этих типов вопросов в качестве отправной точки. http://www.parashift.com/c++-faq-lite/const-correctness.html

0 голосов
/ 15 октября 2010

В вопросе есть несколько вариантов возврата string против char *.То, что я думаю, не имеет отношения к обсуждению const (дайте мне знать, если это не так);Я рассматриваю string возвратные варианты.

class MyClass
{
  string ToString();              // A
  string & ToString();            // B   (I added this)
  const string& ToString();       // C 
  const string& ToString() const; // D
};

Мои предпочтения в таком порядке: D, C, B, A.

Во всех вариациях используются constдвумя способами:

  • спецификатор возвращаемого типа [это первое const в D]

    Это означает, что возвращенный объект нельзя использовать для изменения возвращаемого объекта.Предложение звучит смешно, не правда ли?Ну, могут быть другие способы изменения объекта, и это const не может остановить это.См. Этот FAQ пункт .

  • инвариант класса [второй const в D]

    Это означает, что метод не изменяется (изменить) объект класса.

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

Среди остальных B и C предпочтительнее, чем A, поскольку B и C возвращают ссылку, что позволяет избежать создания копии, требуемой в A.

Между B и C, C предпочтительнее, посколькуон возвращает const ссылку.

0 голосов
/ 15 октября 2010

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

const после скобок () означает, что функция не изменит объект, для которого вы ее вызываете. Обычно то, что называется ToString, не меняет объект, поэтому во всех случаях вам, вероятно, нужен метод const.

Разница между возвратом string и возвратом const string& заключается в том, что ссылка не создает и не копирует объект и может быть быстрее, но вы можете сделать это, только если у вас уже есть объект string (например, как частный участник MyClass). Если нет (скажем, вам нужно собрать воедино несколько битов данных и затем вернуть эту строку), вам нужно будет вернуть string по значению.

Использование string объектов обычно предпочтительнее, чем использование указателей char* в стиле C, но поскольку вы спрашиваете: четвертый, char*, позволит другому коду изменять символы в возвращаемой строке, что, вероятно, плохо. const char*, char const* и const char const* - это одно и то же. char *const технически отличается, но он не будет иметь большого значения в качестве возвращаемого типа по той же причине, по которой возвращение const int не имеет большого значения: вызывающая сторона может просто скопировать и затем изменить возвращаемое значение.

Короче говоря, лучшие формы будут в порядке:

const string& ToString() const;
string ToString() const;
const char* ToString() const;
0 голосов
/ 15 октября 2010

Какое эмпирическое правило вы используете, чтобы решить, когда что-то должно быть постоянным или нет?

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

Третий, вероятно, бессмыслица, потому что ссылки всегда постоянны, верно?

Нет, это не правильно. Ссылка - это псевдоним, а не переменная. Следовательно, вы не можете изменить, на какую переменную указывает «ссылка», как это можно сделать с помощью указателя. Однако у вас может быть ссылка на изменяемый объект (std::string&).

Какая разница между всеми этими методами ToString?

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

char* ToString();

Это ont возвращает указатель на изменяемый массив символов (предположительно, внутреннего состояния).

Обратите внимание, что семья:

char const* ToString();
const char* ToString(); // or this?
const char const* ToString(); // Is this legal?

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

Следующие 2 являются предпочтительным способом (при условии дополнительного const в конце) возврата строк в C ++:

string ToString(); // this one?
const string& ToString(); // or this?

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

Следующие два верны, но я все же рекомендую использовать std::string

const char* ToString() const; // or this?
const char const* ToString() const; // how about this?
0 голосов
/ 15 октября 2010

Я нашел это , чтобы быть полезным руководством

...