2016-12-12 4 views
2

У меня есть файл Constants.h, в котором объявлено и инициализировано около 200 переменных (в основном массивов). Я использую пространство имен.Слишком много декларации и инициализации в заголовочном файле C++

СПОСОБ 1:

//Constants.h 

#ifndef CONSTANTS_H_ 
#define CONSTANTS_H_ 

namespace LibConstants 
{ 
    const int a = 12; 
    const std::string theme[2]; = {"themeA", "themeB"}; 
    const int arr[2] = {1, 2}; 
    // and around 200 more declarations with initialization 
} 
#endif 

Этот .h файл почти каждый .cpp файл, но каждый раз, когда только очень минимальные переменные используется как LibConstants::theme[0]#include.

Мои способы работают нормально, но не он излишне выделяет память? Должен ли я следовать подходу, определять только переменные в файле .h и инициализировать его в .cpp?

Как и в ниже код: МЕТОД 2:

//Constants.h 

#ifndef CONSTANTS_H_ 
#define CONSTANTS_H_ 

namespace LibConstants { 

    std::string getMyTheme(int arg); 
    std::string getMyThemeName(int arg); 

    const int a; 
    const std::string theme[2]; 
    const int arr[2]; 
    // and around 200 more declarations with initialisation 
}; 
#endif 

Инициирование в CPP файле

//Constants.cpp 

#include LibConstants.h 
using namespace LibConstants { 
    std::string getMyTheme(int arg) { 
     theme[2] = {"themeA", "themeB"}; 
     return theme[arg]; 
    } 
    std::string getMyThemeName(int arg) { 
     ... 
    } 
} 

Используя его как

//HelloWorld.cpp 

#include Constants.h 
int main() { 
    //METHOD 1: 
    std::string a = LibConstants::theme[0]; // arg=0 is dynamic and is being read from localStorage in my case. 
    //METHOD 2: 
    std::string a = LibConstants::getMyTheme(0); //Here also arg=0 is dynamic. 
    ... 
} 

Здесь ненужное выделение памяти для ненужных переменных не произойдет, за исключением массивов, объявленных как const std::string st[2]; в файле заголовка.

Здесь «arg = 0» - это время выполнения. Имеет ли значение, если какая-либо переменная не зависит от времени выполнения, а только время компиляции, в этом случае она просто заменит значение placeholder в соответствующем файле .cpp?

Пожалуйста, исправьте меня везде, где я ошибаюсь.

+0

Константы времени компиляции относятся к сегменту данных в большинстве реализаций, независимо от того, объявляете ли вы и инициализируете их в заголовках или исходных файлах. Например, у вас есть 'int a [] = {/ * большой массив * /}'. 'a' не является константой, но его инициализация все равно должна идти куда-то, и программе все равно придется ее загрузить. Поэтому в этом случае я не думаю, что это имеет существенное значение. С другой стороны, если переменная требует выполнения выполнения во время выполнения, либо с помощью конструктора, либо с помощью других средств. Лучше обернуть его функцией и создать его по требованию. –

+0

@YanZhou Спасибо за быстрый ответ. Итак, вы имеете в виду, что оба вышеописанных метода почти одинаковы, поскольку оба они являются временем компиляции и не потребляют память, а заменяют их заполнители в каждом .cpp-файле? – myDoggyWritesCode

+0

№ Дайте минуту, чтобы составить ответ. –

ответ

2

Возьмите пример std::string. Рассмотрим,

namespace Constants { 
const std::string str = "A not very short string"; 
} 

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

С другой стороны, если у вас есть,

const std::string &str() 
{ 
    const static std::string s = "A not very short string"; 

    return s; 
} 

Теперь у вас есть статический локальные переменный. Он будет инициализирован при первом входе функции. И он будет инициализирован только один раз. Если str никогда не вызывается, он не будет инициализирован вообще.

Однако обратите внимание, что строковый литерал "A not very short string" по-прежнему будет занимать некоторую память. Где и как это хранилище, определяется реализация. Обычно в сегментах данных, и воздействие обычно минимально.

В отличие от типов классов. Предпочтительно определять основные типы, особенно интегральные типы в заголовке.

Например,

namespace Constants { 
constexpr int x = 10; 
} 

оптимизирующий компилятор скорее всего не хранить переменную x и его содержимое 10 вообще. Вместо этого, где используется x, он будет заменен на 10 и в некоторых случаях закодирован в операционный код инструкции (так называемые непосредственные операнды). Конечно, это снова деталь реализации. И такую ​​оптимизацию нельзя использовать со всеми компиляторами. И если вы принимаете адрес x или в противном случае используется ODR, компилятор будет вынужден освободить место для этой переменной в любом случае. Но суть в том, что будет очень маловероятно, что вам будет хуже, объявив эти константы в заголовке, чем определяя его во внешнем исходном файле.

+0

Почему предпочтительнее определить интегральные типы для объявления переменных? – myDoggyWritesCode

+0

Посмотрите, правильно ли я понял. Кроме того, это объясняет, что я написал в последнем из моих вопросов. Да, оптимизирующий компилятор не будет хранить переменную x и значение 10, потому что в этом случае вовремя участия не существует. Но в тех случаях, когда участие во время работы присутствует, как в моем случае, когда я получаю аргумент из логического вычисления/локального хранилища, полезно получить их через функцию. Через функцию я имею в виду либо ваш путь const std :: string & str(), либо мой метод 2. Правильно ли я? Если, поэтому, я думаю, что следующий метод 2 лучше, так как большинству переменных в моем файле требуется логика времени выполнения. – myDoggyWritesCode

+1

@ user3241111 Если вы храните интегральную переменную в исходном файле, это потребует не только небольшого объема памяти, но и предотвращения компиляции для оптимизации. Например, скажем, интеграл позже используется как счетчик циклов. Если компилятор видит его в заголовке и видит, что он мал, он может эффективно развернуть цикл. Но если вы только объявляете это, но определяете его в исходном файле, компилятор не сможет этого сделать. К вашему второму комментарию ответ - да. Ключ должен помнить, что глобальные переменные области пространства имен и должны быть инициализированы независимо от того, используются ли они позже. –