Неразрешенный внешний символ для статических членов класса

Проще говоря:

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

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

class test 
{
public:
    static unsigned char X;
    static unsigned char Y;

    ...

    test();
};

test::test() 
{
    X = 1;
    Y = 2;
}

Я новичок в C++, так что не беспокойтесь. Почему я не могу этого сделать?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
137
0
133 924
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Если вы используете C++ 17, вы можете просто использовать спецификатор inline (см. https://stackoverflow.com/a/11711082/55721)


При использовании более старых версий стандарта C++ необходимо добавить определения, соответствующие вашим объявлениям X и Y

unsigned char test::X;
unsigned char test::Y;

где-то. Возможно, вы захотите также инициализировать статический член

unsigned char test::X = 4;

и снова вы делаете это в определении (обычно в файле CXX), а не в объявлении (которое часто находится в файле .H)

Если вы пишете библиотеку только для заголовков, вы можете использовать этот метод, чтобы избежать файла cpp: stackoverflow.com/questions/11709859/…

Shital Shah 18.11.2016 21:49

Объявления статических элементов данных в объявлении класса не являются их определением. Чтобы определить их, вы должны сделать это в файле .CPP, чтобы избежать дублирования символов.

Единственные данные, которые вы можете объявлять и определять, - это интегральные статические константы. (Значения enums также могут использоваться как постоянные)

Возможно, вы захотите переписать свой код как:

class test {
public:
  const static unsigned char X = 1;
  const static unsigned char Y = 2;
  ...
  test();
};

test::test() {
}

Если вы хотите иметь возможность изменять свои статические переменные (другими словами, когда неуместно объявлять их как const), вы можете разделить свой код между .H и .CPP следующим образом:

.H:

class test {
public:

  static unsigned char X;
  static unsigned char Y;

  ...

  test();
};

.CPP:

unsigned char test::X = 1;
unsigned char test::Y = 2;

test::test()
{
  // constructor is empty.
  // We don't initialize static data member here, 
  // because static data initialization will happen on every constructor call.
}

почему здесь, в .CPP, это "unsigned char test :: X = 1;" вместо "test :: X = 1;"? статическая переменная X уже определена, зачем еще нужен "unsigned char"? @sergtk

Penny 19.09.2018 01:56

@Penny Потому что "test :: X = 1;" интерпретируется как задание, тогда как то, что мы пытаемся сделать, - это определение.

Anonymous1847 29.04.2020 09:42

Поскольку это первый поток SO, который, казалось, возник у меня при поиске «неразрешенных внешних элементов со статическими константными элементами» в целом, я оставлю здесь еще один намек для решения одной проблемы с неразрешенными внешними элементами:

Что касается меня, то я забыл пометить определение моего класса __declspec(dllexport), и при вызове из другого класса (за пределами границ dll этого класса) я, конечно, получил мою неразрешенную внешнюю ошибку. Тем не менее, легко забыть, когда вы меняете внутренний вспомогательный класс на класс, доступный из другого места, поэтому, если вы работаете в динамически связанном проекте, вы также можете это проверить.

в моем случае я объявил одну статическую переменную в файле .h, например

//myClass.h
class myClass
{
static int m_nMyVar;
static void myFunc();
}

и в myClass.cpp я попытался использовать эту m_nMyVar. Получена ошибка LINK, например:

ошибка LNK2001: неразрешенный внешний символ «общедоступный: статический класс ... Файл cpp, связанный с ошибкой ссылки, выглядит так:

//myClass.cpp
void myClass::myFunc()
{
myClass::m_nMyVar = 123; //I tried to use this m_nMyVar here and got link error
}

Поэтому я добавляю код ниже поверх myClass.cpp

//myClass.cpp
int myClass::m_nMyVar; //it seems redefine m_nMyVar, but it works well
void myClass::myFunc()
{
myClass::m_nMyVar = 123; //I tried to use this m_nMyVar here and got link error
}

тогда LNK2001 исчезнет.

В моем случае я использовал неправильную привязку.
Это управлялось C++ (cli), но с собственным экспортом. Я добавил в компоновщик -> ввод -> ресурс ссылки сборки dll библиотеки, из которой экспортируется функция. Но для встроенного связывания C++ требуется файл .lib, чтобы правильно «видеть» реализации в cpp, поэтому мне помогло добавить файл .lib в компоновщик -> ввод -> дополнительные зависимости.
[Обычно управляемый код не использует экспорт и импорт dll, он использует ссылки, но это была уникальная ситуация.]

Другие вопросы по теме