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

Я пытаюсь определить константу 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.

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

Спасибо

Ответы [ 7 ]

10 голосов
/ 06 апреля 2009

Тип перечисления не предназначен для определения числовой константы, хотя он (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; }
 };

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

9 голосов
/ 06 апреля 2009

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

static const int BUFFER_LENGTH = 12;

или

enum { BUFFER_LENGTH = 12 };

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

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

3 голосов
/ 06 апреля 2009

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

2 голосов
/ 06 апреля 2009

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

Так что, если вы используете античный компилятор или хотите поддержать их, используйте enum-hack. (Я понятия не имею, сколько лет они должны быть, чтобы не поддерживать статический констант)

1 голос
/ 06 апреля 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

1 голос
/ 06 апреля 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.

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

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

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

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

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

0 голосов
/ 01 мая 2009

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

...