Защо статичните променливи трябва да се декларират два пъти в C++

Имам заглавка, наречена filepaths.h, която дефинира редица статични променливи:

#ifndef FILEPATHS_H
#define FILEPATHS_H

class FilePaths {

public:

    static QString dataFolder();
    static QString profileFolder();

private:

    static QString dataFolder_;
    static QString profileFolder_;

};

}
#endif // FILEPATHS_H

И имам асоцииран filepaths.cpp, който първоначално изглеждаше така:

#include "FilePaths.h"

QString FilePaths::dataFolder() {
    return dataFolder_;
}

QString FilePaths::profileFolder() {
    return profileFolder_;
}

Това обаче не проработи - получих грешка на линкера „неразрешена символна грешка“ на всички статични променливи. Така че добавих тези променливи към C++ файла по следния начин:

#include "FilePaths.h"

QString FilePaths::dataFolder_ = "";
QString FilePaths::profileFolder_ = "";

QString FilePaths::dataFolder() {
    return dataFolder_;
}

QString FilePaths::profileFolder() {
    return profileFolder_;
}

И това работи, но не разбирам защо.

Защо тези статични променливи трябва да бъдат дефинирани два пъти? Или може би не ги дефинирам, а ги инициализирам? Но все пак защо трябва да се направи? Или трябва да напиша класа си по различен начин?


person laurent    schedule 06.08.2011    source източник


Отговори (4)


Едното е дефиниция, другото е декларация. Разликата е, че декларациите могат да се появят няколко пъти, а за променливи, които не са в клас, може би никога, докато дефинициите могат да се появят веднъж и само веднъж.

Причините за нуждата от отделни декларации и дефиниции е архаичната история, нещото, при което всъщност изобщо не трябва да е така, но е така, така че C++ да е съвместим със C, който е проектиран да бъде компилиран в 1970 г.

person Puppy    schedule 06.08.2011
comment
Не мисля, че е толкова архаично. Декларациите казват, че съществува някъде променлива/клас/функция с име X, докато дефинициите казват, че X се намира тук, компилаторът, моля, разпределете място за съхранение за него. Възможността за разделяне на декларации и дефиниции е това, което позволява множество единици за превод в статично типизиран език, без да е необходимо компилаторът да стане прекалено „умен“ и да отгатне вашите намерения. - person vsekhar; 06.08.2011
comment
Не е наистина архаично, тъй като и двата стандарта (дори сега, C99/C++11) са много неясни за действителния процес на свързване и дефиницията в определен cpp файл ще доведе до една дефиниция в получения обектен файл, ако това е било не е така, линкерът няма да намери уникална препратка, когато свързва целия код заедно. - person rubenvb; 06.08.2011
comment
vsekhar, @rubenvb: Бих отбелязал, че за кода на шаблона получаваме множество дефиниции (в обектни файлове) на функции и статични атрибути, но линкерът ги управлява правилно. - person Matthieu M.; 06.08.2011
comment
Архаичната част е именно моделът на изграждане, в който на някои части от програмата трябва да се каже какво съществува в други части от програмата. Компилаторът на Java, например, може да отиде и да намери всичко, което трябва да знае, и така интерфейсите могат да се използват според нуждите на програмиста, а не на компилатора. Има някои предимства на модела C. Някои от тях продължават да съществуват и до днес, въпреки че е под въпрос дали си заслужават разходите. Но много от първоначалните предимства бяха свързани с остарели ограничения на ресурсите по време на компилиране и не се запазват до наши дни. - person Steve Jessop; 06.08.2011
comment
Архаично е, защото програмистът изобщо трябва да се занимава с него. Почти всички други компилирани езици не се нуждаят от този вид система и необходимостта от многократно анализиране на едни и същи декларации отново и отново е ужасно бавно. - person Puppy; 07.08.2011
comment
@vsekhar можем ли да приложим вашия коментар към нестатичните променливи? - person Zac; 18.12.2014

От http://weblogs.asp.net/whaggard/archive/2004/11/05/252685.aspx:

Трябва да го декларирате извън класа, защото в противен случай компилаторът не знае към коя транслационна единица (следователно обектен файл) членът трябва да отиде.

Защото, както каза DeadMG, можете да декларирате променлива много пъти, но да я дефинирате само веднъж. Мисля, че това е като прототипи на функции: можете да имате колкото искате от тях, но само един може да върви с тяло и действително да дефинира функцията.

person Seth Carnegie    schedule 06.08.2011

Не ги декларирате два пъти, декларацията се случва в заглавката на класа, а дефиницията - точката, където променливата всъщност е там и ще разпредели малко памет - е в частта .cpp.

Но разликата с общата променлива на екземпляра е налице, статичната част е там само веднъж на клас за всеки екземпляр, който създавате.

person Wizz    schedule 06.08.2011

Това е така, защото когато някога декларирате клас, вие декларирате структура за конкретните екземпляри на този клас, НО в случай на статични променливи в клас, те са тези, които могат да бъдат инициализирани, преди който и да е обект от класа да бъде инициализиран създадено. ВИЖТЕ, че няма запазено място, когато декларираме клас в паметта, но мястото е запазено, когато декларираме обекта на класа. НИТО един член на класа не може да бъде иницииран като int a=2; но това може да се направи като 'static int a=2;' е възможно в декларацията на класа, резервирайте място за тях във втората декларация, & трябва да сте запознати с това

person nobalG    schedule 06.08.2011