Как объявить статический символ константы* в Вашем заголовочном файле?

Также проверьте алгоритм Бойера-Мура для сопоставления с одной строкой.

57
задан Mark 28 October 2009 в 18:24
поделиться

8 ответов

Вам необходимо определить статические переменные в единице перевода, если они не относятся к целочисленным типам.

В заголовке:

private:
    static const char *SOMETHING;
    static const int MyInt = 8; // would be ok

В файле .cpp:

const char *YourClass::SOMETHING = "something";

Стандарт C ++, 9.4.2 / 4:

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

66
ответ дан 24 November 2019 в 19:24
поделиться
class A{
public:
   static const char* SOMETHING() { return "something"; }
};

Я делаю это все время - особенно для дорогих параметров const по умолчанию.

class A{
   static
   const expensive_to_construct&
   default_expensive_to_construct(){
      static const expensive_to_construct xp2c(whatever is needed);
      return xp2c;
   }
};
9
ответ дан 24 November 2019 в 19:24
поделиться

Ошибка заключается в том, что вы не можете инициализировать static const char * внутри класса. Здесь вы можете инициализировать только целочисленные переменные.

Вам нужно объявить переменную-член в классе, а затем инициализировать ее вне класса:

// файл заголовка

class Foo {
    static const char *SOMETHING;
    // rest of class
};

// файл cpp

const char *Foo::SOMETHING = "sommething";

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

Напротив, char * переменная указывает на реальный объект в памяти, который должен действительно существовать, и это ' s определение (включая инициализацию), которое делает объект существующим. «Правило одного определения» означает, что вы не хотите помещать его в заголовок, потому что тогда все единицы перевода, включая этот заголовок, будут содержать определение. Их нельзя было связать вместе, даже если строка содержит одни и те же символы в обоих, потому что в соответствии с текущими правилами C ++ вы определили два разных объекта с одним и тем же именем, и это незаконно. Тот факт, что в них есть одни и те же символы, не делает это законным.

даже если строка содержит одинаковые символы в обоих, потому что в соответствии с текущими правилами C ++ вы определили два разных объекта с одним и тем же именем, и это незаконно. Тот факт, что в них есть одни и те же символы, не делает это законным.

даже если строка содержит одинаковые символы в обоих, потому что в соответствии с текущими правилами C ++ вы определили два разных объекта с одним и тем же именем, и это незаконно. Тот факт, что в них есть одни и те же символы, не делает это законным.

17
ответ дан 24 November 2019 в 19:24
поделиться

Чтобы ответить на вопрос OP о том, почему это разрешено только с целыми типами.

Когда объект используется как lvalue (то есть как нечто, имеющее адрес в хранилище), он должен удовлетворять «правилу одного определения» (ODR), т.е. он должен быть определен в одной и только одной единице перевода. Компилятор не может и не будет решать, в какой единице трансляции определять этот объект. Это ваша ответственность. Определяя этот объект где-то, вы не просто определяете его, вы фактически сообщаете компилятору, что хотите определить его здесь , в этой конкретной единице трансляции.

Между тем в языке C ++ интегральные константы имеют особый статус. Они могут составлять интегральные постоянные выражения (ICE). В ICE интегральные константы используются как обычные значения , а не как объекты (т.е. не имеет значения, имеет ли такое целое значение адрес в хранилище или нет). Фактически, ICE оцениваются во время компиляции. Чтобы облегчить такое использование интегральных констант, их значения должны быть видны глобально. Да и самой константе действительно не нужно реальное место в хранилище. Из-за этого интегральные константы получили особую обработку: было разрешено включать их инициализаторы в файл заголовка, а требование предоставить определение было ослаблено (сначала де-факто, затем де-юре).

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

27
ответ дан 24 November 2019 в 19:24
поделиться

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

(обратите внимание, это уродливый пример, но дословно работает по крайней мере в g ++ 4.6.1.)

(файл values.hpp)

#include <string>

template<int dummy>
class tValues
{
public:
   static const char* myValue;
};

template <int dummy> const char* tValues<dummy>::myValue = "This is a value";

typedef tValues<0> Values;

std::string otherCompUnit(); // test from other compilation unit

(main.cpp)

#include <iostream>
#include "values.hpp"

int main()
{
   std::cout << "from main: " << Values::myValue << std::endl;
   std::cout << "from other: " << otherCompUnit() << std::endl;
}

(other.cpp)

#include "values.hpp"

std::string otherCompUnit () {
   return std::string(Values::myValue);
}

Скомпилируйте (например, g ++ -o main main.cpp other.cpp && ./main) и увидите две единицы компиляции, ссылающиеся на одну и ту же константу, объявленную в заголовке:

from main: This is a value
from other: This is a value

В MSVC вместо этого вы можете возможность использовать __declspec (selectany)

Например:

__declspec(selectany) const char* data = "My data";
3
ответ дан 24 November 2019 в 19:24
поделиться

Если вы используете Visual C ++, вы можете сделать это непереносимо, используя подсказки для компоновщика ...

// In foo.h...

class Foo
{
public:
   static const char *Bar;
};

// Still in foo.h; doesn't need to be in a .cpp file...

__declspec(selectany)
const char *Foo::Bar = "Blah";

__ declspec (selectany) означает, что даже если Foo :: Bar будет объявлен в нескольких объектных файлах, компоновщик выберет только один.

Имейте в виду, что это будет работать только с набором инструментов Microsoft. Не ожидайте, что это будет портативным.

3
ответ дан 24 November 2019 в 19:24
поделиться

Инициализатор константы разрешен стандартом C ++ только для целочисленных или перечислимых типов. Для получения подробной информации см. 9.4.2 / 4:

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

И 9.4.2 / 7:

Статические элементы данных инициализируются и уничтожаются точно так же, как нелокальные объекты (3.6.2 , 3.6.3).

Итак, вы должны написать где-нибудь в файле cpp:

const char* SomeClass::SOMETHING = "sommething";
1
ответ дан 24 November 2019 в 19:24
поделиться

Чтобы ответить на вопрос почему , интегральные типы являются особенными в том смысле, что они не являются ссылкой на выделенный объект, а скорее значениями, которые дублируются и копируются. Это просто решение реализации, принятое при определении языка, которое должно было обрабатывать значения вне объектной системы и максимально эффективно и «встроенным» способом.

Это не совсем объясняет, почему они разрешены в качестве инициализаторов в тип, но воспринимайте его как #define , и тогда он будет иметь смысл как часть типа, а не как часть объекта.

0
ответ дан 24 November 2019 в 19:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: