Лучший подход для определения константы (используемой в постоянном выражении) в классе?

Я пытаюсь определить константу BUFFER_LENGTH для моего класса для данного варианта использования.

//1. Using preprocessor declaration
//#define BUFFER_LENGTH  12

//2.Global constant
//const int BUFFER_LENGTH  = 12;
class MyRequest
{
public:
    //3. Define an in-class constant
    //static const int BUFFER_LENGTH = 12;

    //4. Declare an enum constant
    enum 
    {
        BUFFER_LENGTH = 12
    };

    MyRequest()
    {
        strcpy(mBuffer, "TestString");
        printf("Buffer: %s, BUFFER_LENGTH = %d",mBuffer, BUFFER_LENGTH);
    }
private:
    char mBuffer[BUFFER_LENGTH];
};

Я только что перечислил различные способы определения константы для класса.

1. Using Preprocessor constant
2. Using Global constant
3. Using in-class constant
4. using an enum.

Какой из них является лучшим подходом к определению констант для данного варианта использования? Я предпочитаю использовать константу перечисления по сравнению с другими подходами. Есть ли другой лучший подход, который я пропустил.

Спасибо,


person aJ.    schedule 06.04.2009    source источник


Ответы (7)


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

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

 class WithConst {
 public:

     // 1. as a const static member variable
     static const int sc_nNumber = 100; // I can initialize here for
                                        // integral-types only

     // 2. as a static member function - possibly inlined.
     static int sf_nNumber() { return 100; }
 };

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

person xtofl    schedule 06.04.2009
comment
Второй пример не является постоянным целым выражением в соответствии с действующими правилами стандарта и поэтому не может использоваться, например, в качестве индекса массива. - person Richard Corden; 06.04.2009
comment
Вам придется немного подождать, прежде чем static constexpr int sf_nNumber() { return 100; } является допустимым константным выражением. - person Eclipse; 06.04.2009
comment
@Джош: действительно! C++0x определяет ключевое слово constexpr, чтобы аннотировать это. (как-то прозвенел звонок) - person xtofl; 06.04.2009

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

static const int BUFFER_LENGTH = 12;

or

enum { BUFFER_LENGTH = 12 };

внутри определения класса.

В первом нет особой пользы, за исключением того, что вы можете явно управлять типом. enum заставляет C++ выбирать для вас неопределенный целочисленный «базовый тип» — он может быть таким же маленьким, как char, если ваше перечисление содержит только небольшие значения, хотя эмпирически большинство компиляторов по умолчанию будут использовать int.

person j_random_hacker    schedule 06.04.2009

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

person sharptooth    schedule 06.04.2009

Я читал в "Thinking in C++", что "enum-hack" использовался в прошлом, потому что некоторые компиляторы не поддерживали члены класса "static const":
http://www.linuxtopia.org/online_books/programming_books/thinking_in_c++/Chapter08_023.html

Поэтому, если вы используете устаревший компилятор или хотите его поддерживать, используйте enum-hack. (Я понятия не имею, НАСКОЛЬКО они должны быть старыми, чтобы не поддерживать статическую константу)

person qwerty    schedule 06.04.2009
comment
У меня есть компилятор VC6. Я не поддерживаю члены класса static const! - person aJ.; 06.04.2009

Я не уверен, что они полностью взаимозаменяемы в этом конкретном случае. Поскольку вы основываете размер члена массива на константе, я считаю, что это должно быть перечисляемое значение. У меня нет времени искать его в стандарте, но я был бы удивлен, если бы вы могли использовать элемент int в качестве размера массива, даже если он равен static const, поскольку фактическое значение может быть не видно в заголовке.

// === in myclass.h
class MyClass {
public:
    static const int MY_SIZE;
private:
    int ary[MY_SIZE];
};

// === in myclass.cpp
/*static*/ const int MyClass::MY_SIZE = 10;

// === in otherclass.cpp
void OtherClass::operation(MyClass& obj) {
    std::cout << "Sizeof(MyClass) = " << sizeof(obj) << std::endl;
}

Я не думаю, что компилятор может скомпилировать otherclass.cpp, не скомпилировав myclass.cpp.

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

Редактировать

Я только что взглянул на Standard, пока ел обед (спасибо за подталкивание, Дэвид)

целочисленное константное выражение может включать только литералы (2.13), перечислители, const переменные или статические элементы данных целочисленного или перечисляемого типов, инициализированные константными выражениями (8.5), нетиповые параметры шаблона интегрального или перечисляемого типы и sizeof выражения.

Похоже, что и константа, и перечисление будут работать при условии, что вы предоставите инициализатор константы в объявлении (или это определение?).

person D.Shawley    schedule 06.04.2009
comment
В стандартном C++ const int является константой времени компиляции и может использоваться как таковая. Вы должны инициализировать объявление. - person David Thornley; 06.04.2009

Использование const int может быть менее эффективным, чем использование enum, ниже приведена программа, демонстрирующая это. Скомпилированный с помощью Metrowerks CodeWarrior 8.x для Windows, он показывает, что объект класса, который определяет константу как const int, занимает на 4 байта памяти больше, чем аналогичный класс, который определяет эту константу как enum:

#include <stdio.h>
#include <stdlib.h>

class foo {
    enum { MY_CONST = 1 };
    int x;
    public:
    foo();
};
class bar {
    const int MY_CONST;
    int x;
    public:
    bar();
};

int main() {
    printf( "%u %u\n", sizeof( foo), sizeof( bar));
    return EXIT_SUCCESS;
}

Производит: "4 8"

Конструкторы были добавлены для предотвращения агрессивной оптимизации компилятором. Поскольку программа не создает объекты этих типов, конструкторы не обязательно определять в отдельном модуле компиляции. То же самое верно для const int

person dmityugov    schedule 06.04.2009

Читая предыдущие сообщения, я заметил, что никто не упомянул трюк с «перечислением в пространстве имен». Если вам нравятся перечисления, потому что вы работаете со старым компилятором (у меня был такой опыт работы с платформой встроенного программирования VxWorks/Tornado), то послушайте: вы можете избежать конфликтов имен, поместив перечисление внутри пространства имен или (если пространства имен не являются поддерживается либо) внутри класса.

person GregC    schedule 01.05.2009