Строковые константы в с++
От: steep8  
Дата: 15.08.09 16:08
Оценка:
Интересует как кто делает константы строк в класса, желательно чтобы можно было использовать как Class::String.
У меня пока используется так

class A
{
public:
A(){A::str = "string";}


const char * str;
};

но использовать как Class::str такое конечно нельзя, хотелось бы другие варианты.
Re: Строковые константы в с++
От: Ventalf Россия  
Дата: 15.08.09 16:38
Оценка:
Здравствуйте, steep8, Вы писали:

S>Интересует как кто делает константы строк в класса, желательно чтобы можно было использовать как Class::String.

S>У меня пока используется так
S> [...]
S>но использовать как Class::str такое конечно нельзя, хотелось бы другие варианты.

Ты хочешь статический константный член в класс добавить? Тогда надо делать при объявлении класса:

class A
{
public:
  ...
  static const char * const str;
}


И где-нибудь (не в хэдере, надо, чтоб было один раз) проинициализируй:

const char * const A::str = "значение";
... << RSDN@Home 1.2.0 alpha 4 rev. 1238>>
Re: Строковые константы в с++
От: vermont  
Дата: 16.08.09 08:04
Оценка:
Здравствуйте,

S>Интересует как кто делает константы строк в класса, желательно чтобы можно было использовать как Class::String.

S>У меня пока используется так
S>но использовать как Class::str такое конечно нельзя, хотелось бы другие варианты.

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

Например.
namespace ClassConstants
{
    const std::string DEFAULT = "default";
}
class Class
{
...
}
Re[2]: Строковые константы в с++
От: quodum  
Дата: 17.08.09 17:05
Оценка: 6 (1) +1
Здравствуйте, vermont, Вы писали:

V>Иногда удобнее хранить константы не в классе, а в отдельном namespace, который можно поместить перед классом.

V>В этом случае описание выглядит более наглядно и все определения хранятся в хедере.

V>Например.

V>
V>namespace ClassConstants
V>{
V>    const std::string DEFAULT = "default";
V>}
V>


Только при этом нужно осозновать последствия такого решения. В частности -- то, что экземпляров ClassConstants::DEFAULT в программе будет ровно столько, сколько "модулей трансляции" (сиречь cpp-файлов) включают этот заголовок. Что не только приводит к транжирству ресурсов, но и может вызвать проблемы, связанные с identity констант.
Re[2]: Строковые константы в с++
От: rg45 СССР  
Дата: 18.08.09 04:54
Оценка: 11 (3)
Здравствуйте, vermont, Вы писали:

V>Здравствуйте,


S>>Интересует как кто делает константы строк в класса, желательно чтобы можно было использовать как Class::String.

S>>У меня пока используется так
S>>но использовать как Class::str такое конечно нельзя, хотелось бы другие варианты.

V>В дополнение к предыдущему посту.

V>Иногда удобнее хранить константы не в классе, а в отдельном namespace, который можно поместить перед классом.
V>В этом случае описание выглядит более наглядно и все определения хранятся в хедере.

V>Например.

V>
V>namespace ClassConstants
V>{
V>    const std::string DEFAULT = "default";
V>}
V>class Class
V>{
V>...
V>}
V>


В дополнение к предыдущему посту У такого решения есть еще один серьезный недостаток. Дело в том, что хоть DEFAULT и определена с модификаторм const, она не является такой же константой как , например, константа типа const int, значение которой формируется на этапе компиляции. А на самом деле это обыкновенная глобальная переменная, доступная только для чтения. И как и у любой глобальной переменной у нее есть фаза инициализацции. А теперь представьте такую ситуацию: вы определяете какой-то собственный класс и при реализации методов этого класса пользуетесь этими константами. После этого в другом модуле создаете еще одну глобальную переменную — Вашего типа. Всё, грабли разложены, можно ходить. Поскольку порядок инициализации глобальных переменных, определенных, в разных модулях не определен, весьма вероятно, что методы Вашего класса будут вызваны раньше, чем успеют проинициализироваться эти "константы", вынесенные в отдельный заголовок. Поэтому применения таких псевдо-констант лучше избегать. Если очень нужно, то можно помещать их определения внутрь функций. А вообще, глобальные переменные зло — если с неопределенностью в очередности инициализации еще как-то можно воевать, то с неопределенностью в порядке деинициализации ничего не поделаешь.
--
Re[3]: Строковые константы в с++
От: steep8  
Дата: 18.08.09 12:54
Оценка:
А вообще, глобальные переменные зло — если с неопределенностью в очередности инициализации еще как-то можно воевать, то с неопределенностью в порядке деинициализации ничего не поделаешь.


Так в итоге как правильно определять константы?
Re[3]: Строковые константы в с++
От: Николай Ивченков  
Дата: 18.08.09 15:46
Оценка:
rg45:

R>В дополнение к предыдущему посту У такого решения есть еще один серьезный недостаток. Дело в том, что хоть DEFAULT и определена с модификаторм const, она не является такой же константой как , например, константа типа const int, значение которой формируется на этапе компиляции. А на самом деле это обыкновенная глобальная переменная, доступная только для чтения. И как и у любой глобальной переменной у нее есть фаза инициализацции. А теперь представьте такую ситуацию: вы определяете какой-то собственный класс и при реализации методов этого класса пользуетесь этими константами. После этого в другом модуле создаете еще одну глобальную переменную — Вашего типа. Всё, грабли разложены, можно ходить. Поскольку порядок инициализации глобальных переменных, определенных, в разных модулях не определен, весьма вероятно, что методы Вашего класса будут вызваны раньше, чем успеют проинициализироваться эти "константы", вынесенные в отдельный заголовок. Поэтому применения таких псевдо-констант лучше избегать.


Странные рассуждения. Избегать следует инициализации, которая осуществляется до входа в main и при этом использует значения нелокальных переменных со статическим временем жизни, определённых в других единицах трансляции. В данном случае инициализация строковой переменной ни от чего не зависит. Если кто-то криво использует эту переменную, то тут уже проблемы не в самой переменной.
Re: Строковые константы в с++
От: IROV..  
Дата: 18.08.09 15:59
Оценка:
Здравствуйте, steep8, Вы писали:
static const std::string & getDefaultString()
{
  static std::string str("Default");
  return str;
}


я не волшебник, я только учусь!
Re[4]: Строковые константы в с++
От: rg45 СССР  
Дата: 18.08.09 16:34
Оценка:
Здравствуйте, Николай Ивченков, Вы писали:

НИ>rg45:


R>>В дополнение к предыдущему посту У такого решения есть еще один серьезный недостаток. Дело в том, что хоть DEFAULT и определена с модификаторм const, она не является такой же константой как , например, константа типа const int, значение которой формируется на этапе компиляции. А на самом деле это обыкновенная глобальная переменная, доступная только для чтения. И как и у любой глобальной переменной у нее есть фаза инициализацции. А теперь представьте такую ситуацию: вы определяете какой-то собственный класс и при реализации методов этого класса пользуетесь этими константами. После этого в другом модуле создаете еще одну глобальную переменную — Вашего типа. Всё, грабли разложены, можно ходить. Поскольку порядок инициализации глобальных переменных, определенных, в разных модулях не определен, весьма вероятно, что методы Вашего класса будут вызваны раньше, чем успеют проинициализироваться эти "константы", вынесенные в отдельный заголовок. Поэтому применения таких псевдо-констант лучше избегать.


НИ>Странные рассуждения. Избегать следует инициализации, которая осуществляется до входа в main и при этом использует значения нелокальных переменных со статическим временем жизни, определённых в других единицах трансляции. В данном случае инициализация строковой переменной ни от чего не зависит. Если кто-то криво использует эту переменную, то тут уже проблемы не в самой переменной.


Ну давайте разберем конкретный пример. Над программой работали три человека. Первый участник все константы собрал в один заголовок. Второй разработал какой-то класс, который в своем конструкторе как-то использует одну из констант. Третий человек разработал API, состоящий исключительно из свободных функций (например, экспортируемых из dll). Для этого в модуле, в котором эти функции определены, ему пришлось создать статический (с внутренней линковкой) экземпляр класса, разработанного вторым участником. Вопрос: кто в данной конкретной ситуации накосячил?
--
Re[4]: Строковые константы в с++
От: rg45 СССР  
Дата: 18.08.09 16:42
Оценка:
Здравствуйте, steep8, Вы писали:

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



S>Так в итоге как правильно определять константы?


Ну один рецепт на все случаи дать невозможно. Критерий прост: программирование должно быть максимально безопасным. В идеале нужно стремиться к тому, чтобы от ошибки уберегала не документация к API, а сам API был так построен, чтоб возможность ошибки была исключена. Понятно, что на практике все делать идеально не возможно, но стремиться к этому все же нужно. В данном конкретном случае строковую константу нужно было бы определить не как const std::string, а как const char* const и проблема бы ушла сама собой.
--
Re[5]: Строковые константы в с++
От: Николай Ивченков  
Дата: 18.08.09 17:24
Оценка:
rg45:

R>Ну давайте разберем конкретный пример. Над программой работали три человека. Первый участник все константы собрал в один заголовок. Второй разработал какой-то класс, который в своем конструкторе как-то использует одну из констант. Третий человек разработал API, состоящий исключительно из свободных функций (например, экспортируемых из dll). Для этого в модуле, в котором эти функции определены, ему пришлось создать статический (с внутренней линковкой) экземпляр класса, разработанного вторым участником. Вопрос: кто в данной конкретной ситуации накосячил?


Третий. Вся сложная инициализация (где могут быть зависимости от инициализации статических объектов в других единицах трансляции) должна осуществляться исключительно в рамках main. Тот, кто хочет попользовать библиотечные функции, может сперва позвать initialize_library(), которая создаст нужный ей объект через new-выражение.

В конце концов, гипотетический четвёртый человек, который во время инициализации ещё одной глобальной переменной воспользуется более "умной" на первый взгляд библиотекой — которая сама себя инициализирует через глобальные переменные, — может наступить на те же грабли независимо от того, как там первые два распорядились с некими константами.
Re[2]: Строковые константы в с++
От: Аноним  
Дата: 18.08.09 19:21
Оценка:
IRO>Здравствуйте, steep8, Вы писали:
IRO>
IRO>static const std::string & getDefaultString()
IRO>{
IRO>  static std::string str("Default");
IRO>  return str;
IRO>}
IRO>

...и бабах в многопоточном приложении
Re[6]: Строковые константы в с++
От: rg45 СССР  
Дата: 18.08.09 21:29
Оценка: +1
Здравствуйте, Николай Ивченков, Вы писали:

НИ>rg45:


R>>Ну давайте разберем конкретный пример. Над программой работали три человека. Первый участник все константы собрал в один заголовок. Второй разработал какой-то класс, который в своем конструкторе как-то использует одну из констант. Третий человек разработал API, состоящий исключительно из свободных функций (например, экспортируемых из dll). Для этого в модуле, в котором эти функции определены, ему пришлось создать статический (с внутренней линковкой) экземпляр класса, разработанного вторым участником. Вопрос: кто в данной конкретной ситуации накосячил?


НИ>Третий. Вся сложная инициализация (где могут быть зависимости от инициализации статических объектов в других единицах трансляции) должна осуществляться исключительно в рамках main. Тот, кто хочет попользовать библиотечные функции, может сперва позвать initialize_library(), которая создаст нужный ей объект через new-выражение.


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

Выходит, что заводить глобальные константы сложных типов можно, но только в том случае, если есть уверенность, что при этом не возникнет зависимости от инициализации статических объектов в других единицах трансляции. Т.е это можно делать только в том случае, если нам известны все детали реализации используемых для этих констант типов. И кроме того, если мы полностью уверены в неизменности реализации этих классов. ИМХО, проще сказать, что этого делать нельзя.
--
Re[3]: Строковые константы в с++
От: inko Россия  
Дата: 19.08.09 09:04
Оценка:
Здравствуйте, Аноним, Вы писали:

IRO>>Здравствуйте, steep8, Вы писали:

IRO>>
IRO>>static const std::string & getDefaultString()
IRO>>{
IRO>>  static std::string str("Default");
IRO>>  return str;
IRO>>}
IRO>>

А>...и бабах в многопоточном приложении

ну смотря чем собирать, утверждается, что в gcc такое создание потокобезопасно))
Re[7]: Строковые константы в с++
От: Николай Ивченков  
Дата: 19.08.09 11:00
Оценка:
rg45:

R>А чем отличается то, что сделал третий от того, что сделал первый? Третий ведь тоже мог по примеру первого вынести свою константу в заголовочный файл. Причем, не важно, внес бы он определение этой константы в существующий заголовок с константами, или создал бы новый. Разница между действиями первого и третьего разработчиков в этом случае свелась бы только к типу определенных ими констант.


Если я правильно понял, речь шла о том, что третий разработчик инициализировал нелокальный объект со статическим временем жизни, не имея гарантий, что такая инициализация происходит корректно. Первый разработчик в отношении своих констант мог располагать такими гарантиями.

R>Выходит, что заводить глобальные константы сложных типов можно, но только в том случае, если есть уверенность, что при этом не возникнет зависимости от инициализации статических объектов в других единицах трансляции. Т.е это можно делать только в том случае, если нам известны все детали реализации используемых для этих констант типов.


То, что некоторая сущность может быть безопасно использована во время инициализации нелокального объекта со статическим временем жизни, вполне может являться документируемой особенностью этой сущности. И хотя в случае std::string подобных гарантий я в стандарте C++ не встречал, я на 99,99% уверен, что нелокальные объекты типа std::string со static storage duration широко используются в программах (хотя бы качестве static data member-ов классовых типов), и поэтому ни один нормальный вендор не будет расставлять здесь грабли.

R>ИМХО, проще сказать, что этого делать нельзя.


Ага, "обтягивающее не носить, белое не надевать" (c)
Re[8]: Строковые константы в с++
От: rg45 СССР  
Дата: 19.08.09 16:28
Оценка:
Здравствуйте, Николай Ивченков, Вы писали:

НИ>То, что некоторая сущность может быть безопасно использована во время инициализации нелокального объекта со статическим временем жизни, вполне может являться документируемой особенностью этой сущности. И хотя в случае std::string подобных гарантий я в стандарте C++ не встречал, я на 99,99% уверен, что нелокальные объекты типа std::string со static storage duration широко используются в программах (хотя бы качестве static data member-ов классовых типов), и поэтому ни один нормальный вендор не будет расставлять здесь грабли.


А теперь посмотрим на проблему с точки зрения второго разработчика (из описанного выше сценария). Как только в конструкторе своего класса он использовал глобальную константу типа std::string, то из сказанного тобой же следует:
А ведь этих неприятностей можно было избежать простой заменой типа константы с const std::string на const char* const.
--
Re[9]: Строковые константы в с++
От: Николай Ивченков  
Дата: 19.08.09 17:55
Оценка:
rg45:

R>А теперь посмотрим на проблему с точки зрения второго разработчика (из описанного выше сценария). Как только в конструкторе своего класса он использовал глобальную константу типа std::string, то из сказанного тобой же следует:

R>Разработанный им класс не является сущностью, которая "может быть безопасно использована во время инициализации нелокального объекта со статическим временем жизни".

Не слишком-то и большая потеря.

R>И он, как добросовестный разработчик обязан это задокументировать. И хорошо, если эту документацию прочитают ДО того, как наступят на грабли.


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

R>Его класс заведомо не найдет "широкого использоваться в программах"


Такого широкого, как std::string, конечно, не найдёт.

R>"Нормальным вендором" назвать его нельзя, потому как "грабли он расставил", все таки.


Это алогичный вывод. std::string — это фундаментальный примитив, входящий в стандартную библиотеку (во многих ЯП строки вообще встроенными типами делают). То, что справедливо в отношении таких примитивов, не обязательно будет справедливо в отношении классов сторонних библиотек.

R>А ведь этих неприятностей можно было избежать простой заменой типа константы с const std::string на const char* const.


Объектами std::string удобнее пользоваться.
Re[5]: Строковые константы в с++
От: Vlad_SP  
Дата: 19.08.09 18:10
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну давайте разберем конкретный пример. [...] Вопрос: кто в данной конкретной ситуации накосячил?


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

PS: само наличие косяка не вызывает сомнений.
Re[3]: Строковые константы в с++
От: D14  
Дата: 19.08.09 18:53
Оценка:
Здравствуйте, rg45, Вы писали:

R>А теперь представьте такую ситуацию: вы определяете какой-то собственный класс и при реализации методов этого класса пользуетесь этими константами. После этого в другом модуле создаете еще одну глобальную переменную — Вашего типа. Всё, грабли разложены, можно ходить. Поскольку порядок инициализации глобальных переменных, определенных, в разных модулях не определен, весьма вероятно, что методы Вашего класса будут вызваны раньше, чем успеют проинициализироваться эти "константы", вынесенные в отдельный заголовок.Поэтому применения таких псевдо-констант лучше избегать.


Можно поподробней, как из посылки "Поскольку порядок инициализации глобальных переменных, определенных, в разных модулях не определен" следует вывод
"Поэтому применения таких псевдо-констант лучше избегать"?
Re[4]: Строковые константы в с++
От: rg45 СССР  
Дата: 19.08.09 21:26
Оценка: 17 (2)
Здравствуйте, D14, Вы писали:

D14>Здравствуйте, rg45, Вы писали:


R>>А теперь представьте такую ситуацию: вы определяете какой-то собственный класс и при реализации методов этого класса пользуетесь этими константами. После этого в другом модуле создаете еще одну глобальную переменную — Вашего типа. Всё, грабли разложены, можно ходить. Поскольку порядок инициализации глобальных переменных, определенных, в разных модулях не определен, весьма вероятно, что методы Вашего класса будут вызваны раньше, чем успеют проинициализироваться эти "константы", вынесенные в отдельный заголовок.Поэтому применения таких псевдо-констант лучше избегать.


D14>Можно поподробней, как из посылки "Поскольку порядок инициализации глобальных переменных, определенных, в разных модулях не определен" следует вывод

D14>"Поэтому применения таких псевдо-констант лучше избегать"?

Можно и поподробней. Пример я постарался сделать максимально упрощенным, поэтому к практическому смыслу используемых в нем сущностей прошу не придираться. Итак, в проекте три файла — один заголовочный и два компилруемых модуля (.cpp). Вот они:
header.h:
//header.h
#include <string>

//Класс Path выпоняет конструирование полного пути к файлу
//путем присодинения имени файла, принимаемого конструктором
//к имени домашней директории, заданной ниже строковой константой
class Path
{
public:
  Path(const std::string& file_name);
  const std::string& path() const;

private:
  std::string m_path;  
};

//Константы
const std::string my_home_directory = "D:\\Documents"; //имя моей домашней директории
const Path my_photo("my_photo.jpg");                   //полный путь к файлу с моей любимой фотографией

B.cpp
//B.cpp
//Реализация класса Path
#include "header.h"

Path::Path(const std::string& file_name)
: m_path(my_home_directory + std::string("\\") + file_name)
{
}

const std::string& Path::path() const
{
  return m_path;
}

A.cpp
//A.cpp
//Использование сущностей, определенных в других файлах
#include <iostream>
#include "header.h"

int main() 
{
  std::cout << my_photo.path() << std::endl;
}


При запуске программы, до входа в main, выполняется инициализация объектов со статическим временем жизни (static storage duration). Такими объектами в этой программе являются константы my_home_directory и my_photo, определенные в заголовочном файле. По умолчанию константы в C++ имеют внутреннюю линковку (internal linkage). Это означает, что в каждом модуле, подключающем заголовочный файл header.h, будут созданы отдельные экземпляры каждой константы. Инициализация же этих констант будет происходить помодульно — сначала один модуль, потом другой. Но какой сначала, какой потом не известно, и в этом-то вся проблема.

Данный пример будет работать правильно при условии, что первым будет проинициализирован модуль, в котором содержится реализация класса Path (модуль B.cpp). В этом случае на консоль будет выведено "D:\Documents\my_photo.jpg", как и задумано.

А вот случай, когда первым инициализируется модуль A.cpp давайте разберем по шагам. В пределах каждого модуля объекты со статическим временем жизни инициализируются в том порядке, в котором они объявлены. Поэтому первой будет проинициализирован экземпляр константы my_home_directory модуля A.cpp. После этого наступает очередь инициализации константы my_photo модуля A.cpp, и вот тут начинается самое интересное. Для этой константы вызывается конструктор класса Path, который определен в модуле B.cpp. В конструкторе класса Path используется константа my_home_directory модуля B.cpp. Но константы модуля B.cpp в данный момент еще не проинициализированы, и что получится при обращении к неинициализированным объектам одному богу известно. В лучшем случае члену m_path будет присвоено совсем не то значение, которое мы ожидаем.

Я прогонял этот пример на VS2005 и VS2008 с STL от Dinkumware. Закономерность в определении порядка инициализации модулей мне так и не удалось понять, но наблюдается зависимость от имен файлов. Вот именно при таких именах файлов первым инициализируется модуль A.cpp и константа my_photo этого модуля получается с дефектом: при выводе на консоль получаем: "\my_photo.jpg".
--
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.