Инициализация члена статического класса

Я пытаюсь инициализировать статический член класса, и мне не повезло. Вот тест:

файл Test.h

#include <string>

class Test {

public:
    static void init(char*);

private:
    static std::string  *sp;

};

файл Test.cpp

#include "Test.h"

// Initialize the class
void
Test::init(char *foo) {
    Test::sp = new std::string(foo);
}

int main(int argc, char** argv) {
    Test::init(argv[1]);  // call the class initializer
}

Компоновщик терпит неудачу с:

Undefined symbols for architecture x86_64:
  "Test::sp", referenced from:
      Test::init(char*) in Test-OK13Ld.o
ld: symbol(s) not found for architecture x86_64

В реальном мире init() проделает реальную работу по установке статического члена. Может ли кто-нибудь указать на ошибку?


person Chap    schedule 15.07.2013    source источник
comment
static std::string *sp; не определено.   -  person user1810087    schedule 15.07.2013
comment
кроме того, зачем использовать указатель на std::string?   -  person billz    schedule 15.07.2013
comment
Что ж, я знаю, что такое undefined ref/unresolved extern, так что эта ссылка довольно широкая. Комментарий @itwasntpete напомнил мне, что у меня есть только декларация. Итак, видимо, мне нужно определение внутри (но вне) определения класса.   -  person Chap    schedule 15.07.2013


Ответы (2)


Это немного смущающая «особенность» C++: вам нужно немного подержать руку, чтобы убедиться, что компоновщик может генерировать символы. Вам нужно выбрать какой-нибудь cpp файл и убедиться, что такая обработка не происходит для одних и тех же символов в любом другом (иначе компоновщик не сработает, когда обнаружит повторяющиеся символы). Поэтому вам нужно сделать еще одно объявление статических переменных-членов для вашего класса в файле cpp следующим образом:

std::string * Test::sp; // or sp = NULL;
person user268396    schedule 15.07.2013
comment
Ах, разница между объявлением и определением. Я никогда не думал о том, где именно будут жить эти статики; Я просто представлял, что C++ позаботится обо всем. Стоит отметить (если это правда!), что определение (или это другое объявление?) должно быть вне каких-либо функций в файле .cpp. - person Chap; 15.07.2013
comment
Это действительно это уродливо! Если написано как оператор присваивания, в какой момент он выполняется? - person Chap; 15.07.2013
comment
Вот тут-то и появляется фиаско термина «статический порядок инициализации». По сути, перед main, но когда именно зависит от всех других переменных static, и вы очень, очень не хотите зависеть от этого. Так что не делайте грязных вещей до main, если можете этого избежать (что бывает почти всегда). Единственная точка установки указателя на NULL заключается в том, что вы можете проверить, правильно ли он уже установлен вашей функцией инициализации. - person user268396; 15.07.2013
comment
Спасибо. Если вы отредактируете свой ответ, чтобы подчеркнуть, что определение статического члена должно быть вне какой-либо функции в .cpp, я поставлю вам галочку. (Я знаю, что в противном случае компилятор будет лаять, но это действительно выглядит странно.) - person Chap; 15.07.2013

Как говорится в сообщении об ошибке, static std::string *sp; должен быть где-то определен, поскольку он не связан ни с одним экземпляром class Test.

Добавление его в Test.cpp в глобальном масштабе решит проблему:

std::string *Test::sp = NULL;
person greatwolf    schedule 15.07.2013