2009-10-14 10 views
354

Chciałbym mieć prywatną stałą statyczną dla klasy (w tym przypadku fabryka kształtu). Chciałbym mieć coś w tym stylu.Stały ciąg znaków C++ (element klasy)

class A { 
    private: 
     static const string RECTANGLE = "rectangle"; 
} 

Niestety otrzymuję wszelkiego rodzaju błędów z C++ (g ++) kompilatora, takich jak:

ISO C++ forbids initialization of member ‘RECTANGLE’

invalid in-class initialization of static data member of non-integral type ‘std::string’

error: making ‘RECTANGLE’ static

To mi mówi, że ten rodzaj konstrukcji użytkownik nie jest zgodny ze standardem. Jak masz prywatną literalną stałą (lub być może publiczną) bez konieczności stosowania dyrektywy #define (chcę uniknąć brzydoty globalności danych!)

Każda pomoc jest doceniana. Dzięki.

+11

Dzięki za wszystkie wspaniałe odpowiedzi! Niech żyje tak! –

+0

Czy ktoś może mi powiedzieć, jaki jest typ "integralny"? Dziękuję Ci bardzo. –

+1

Typy całkowe odnoszą się do typów reprezentujących liczby całkowite. Zobacz http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fintvar.htm – bleater

Odpowiedz

377

Musisz zdefiniować swój statyczny element poza definicją klasy i podać tam inicjator.

Pierwszy

// In a header file (if it is in a header file in your case) 
class A { 
private:  
    static const string RECTANGLE; 
}; 

a następnie

// In one of the implementation files 
const string A::RECTANGLE = "rectangle"; 

Składnia ty pierwotnie próbuje użyć (inicjator wewnątrz definicji klasy) jest dozwolone wyłącznie z integralnych i enum typów.

+5

Ponadto, jeśli nie ma wymogu używania ciągu STL, równie dobrze można zdefiniować const char *. (mniej narzutów) – KSchmidt

+42

Nie jestem pewien, czy zawsze jest to mniej kosztowne - zależy to od użycia. Jeśli ten element ma być przekazywany jako argument do funkcji, które pobierają ciąg stały, zostanie utworzony tymczasowo dla każdego wywołania podczas tworzenia jednego ciągu znaków podczas inicjowania. Koszt narzutu IMHO dla tworzenia statycznego obiektu tekstowego jest pomijalny. –

+17

Wolałbym też używać std :: string. Obciążenie jest znikome, ale masz znacznie więcej opcji i dużo mniej prawdopodobne jest, że napiszesz jakieś głupie rzeczy, takie jak "magia" == A :: PROSTOKĄTNY tylko, aby porównać ich adres ... –

7

To use that in-class initialization syntax, the constant must be a static const of integral or enumeration type initialized by a constant expression.

To jest ograniczenie. Dlatego w tym przypadku musisz zdefiniować zmienną poza klasą. odnoszą answwer z @AndreyT

15

To tylko dodatkowa informacja, ale jeśli naprawdę chcesz ciąg w pliku nagłówkowym, spróbuj coś takiego:

class foo 
{ 
public: 
    static const std::string& RECTANGLE(void) 
    { 
     static const std::string str = "rectangle"; 

     return str; 
    } 
}; 

Choć wątpię, że jest to zalecane.

+0

To wygląda fajnie :) - czy zgaduję, że masz tło w innych językach niż C++? –

+4

Nie polecam tego. Robię to często. Działa to dobrze i uważam, że jest to bardziej oczywiste niż umieszczanie napisu w pliku implementacji. Rzeczywiste dane std :: string nadal znajdują się na stercie. Zwróciłbym const char *, w którym to przypadku nie trzeba zadeklarować zmiennej statycznej, aby deklaracja zajmowała mniej miejsca (w sensie kodowym). Tylko kwestia gustu. – Zoomulator

4

Obecny standard zezwala tylko na taką inicjalizację dla stałych stałych typów całkowych. Musisz to zrobić, jak wyjaśnił AndreyT. Jednak będzie on dostępny w następnym standardzie przez new member initialization syntax.

30

Definicje wewnątrz klasy mogą tylko zadeklarować statycznych członków. Muszą być one zdefiniowane jako poza klasą. W przypadku stałych całkowych czasu kompilacji standard tworzy wyjątek, który umożliwia "inicjowanie" członków. Wciąż nie jest to definicja. Podjęcie adresu nie zadziałałoby na przykład bez definicji.

Chciałbym wspomnieć, że nie widzę korzyści z używania std :: string ponad const char [] dla stałych. std :: string jest niezły i wymaga jedynie dynamicznej inicjalizacji. Tak więc, jeśli napiszesz coś podobnego

const std::string foo = "hello"; 

w zakresie przestrzeni nazw konstruktor foo będą uruchamiane tuż przed realizacją głównych startów i to konstruktor utworzy kopię stałej „cześć” w pamięci sterty. Jeśli naprawdę nie potrzebujesz PROSTOKĄTNY, aby być std :: string, możesz równie dobrze napisać:

// class definition with incomplete static member could be in a header file 
class A { 
    static const char RECTANGLE[]; 
}; 

// this needs to be placed in a single translation unit only 
const char A::RECTANGLE[] = "rectangle"; 

Tam! Bez alokacji sterty, bez kopiowania, bez inicjalizacji dynamicznej.

Pozdrawiam, s.

5

możliwe tylko zrobić:

static const std::string RECTANGLE() const { 
    return "rectangle"; 
} 

lub

#define RECTANGLE "rectangle" 
+7

Używanie #define, gdy można używać stałej pisanej, jest po prostu niepoprawne. –

+0

Twój pierwszy przykład jest w zasadzie dobrym rozwiązaniem, jeśli nie masz 'constexpr', ale nie możesz utworzyć statycznej funkcji' const'. –

+0

Tego rozwiązania należy unikać. Tworzy nowy ciąg przy każdym wywołaniu. Byłoby lepiej: 'static const std :: string RECTANGLE() const {static const std :: string value (" rectangle "); zwracana wartość; } ' –

4

Można też pójść na rozwiązanie const char* wspomniano powyżej, ale jeśli trzeba ciąg cały czas, będziesz mieć dużo kosztów ogólnych.
Z drugiej strony, ciąg statyczny wymaga dynamicznej inicjalizacji, dlatego jeśli chcesz użyć jego wartości podczas inicjowania innej globalnej/statycznej zmiennej, możesz trafić na problem z kolejnością inicjowania. Aby tego uniknąć, najtańszą rzeczą jest uzyskiwanie dostępu do statycznego obiektu tekstowego przez program pobierający, który sprawdza, czy obiekt jest inicjowany czy nie.

//in a header 
class A{ 
    static string s; 
public: 
    static string getS(); 
}; 
//in implementation 
string A::s; 
namespace{ 
    bool init_A_s(){ 
    A::s = string("foo"); 
    return true; 
    } 
    bool A_s_initialized = init_A_s(); 
} 
string A::getS(){  
    if (!A_s_initialized) 
    A_s_initialized = init_A_s(); 
    return s; 
} 

Pamiętaj, aby używać tylko A::getS(). Ponieważ wszelkie wątki mogą być uruchamiane tylko przez main(), a A_s_initialized jest inicjowane przed main(), nie potrzebujesz blokad nawet w środowisku wielowątkowym. A_s_initialized jest domyślnie 0 (przed inicjalizacją dynamiczną), więc jeśli użyjesz getS() zanim s zostanie zainicjalizowane, wywołasz funkcję init w bezpieczny sposób.

Btw, w odpowiedzi powyżej: „static const std :: string prostokąt() const”, funkcje statyczne nie mogą być const ponieważ nie mogą zmienić stan, gdy jakiś obiekt i tak (nie ma ten wskaźnik).

113

w C++ 11 można teraz zrobić:

class A { 
private: 
    static constexpr const char* STRING = "some useful string constant"; 
}; 
+0

Dzięki, że uratowałeś dzień. Potrzebowałem bitset wielkości statycznej tablicy w mojej klasie. Nie jest możliwe bez constexpr (dla tablicy), chyba że chcesz alokować sterty za każdym razem, gdy klasa jest tworzona. – rwst

+15

Niestety to rozwiązanie nie działa dla std :: string. – SmallChess

+0

Należy zauważyć, że 1. to działa tylko z literałami i 2. nie jest to standard zgodny, chociaż Gnu/GCC uzupełnia kary, inne kompilatory rzucają błąd. Definicja musi być w ciele. – ManuelSchneid3r

3

klasy zmienne statyczne mogą być oświadczył w nagłówku, ale musi być zdefiniowane w pliku .cpp. Dzieje się tak dlatego, że może istnieć tylko jedno wystąpienie zmiennej statycznej, a kompilator nie może zdecydować, w którym wygenerowanym pliku obiektowym umieścić, aby zamiast tego podjąć decyzję.

Aby zachować definicję wartości statycznej za pomocą deklaracji w C++ 11 , można użyć zagnieżdżonej struktury statycznej. W tym przypadku element statyczny jest strukturą i musi być zdefiniowany w pliku .cpp, ale wartości znajdują się w nagłówku.

class A 
{ 
private: 
    static struct _Shapes { 
    const std::string RECTANGLE {"rectangle"}; 
    const std::string CIRCLE {"circle"}; 
    } shape; 
}; 

Zamiast inicjowania poszczególnych członków całą strukturę statyczną jest inicjowany w .cpp:

A::_Shapes A::shape; 

Wartości są dostępne z

A::shape.RECTANGLE; 

lub - ponieważ członkowie są prywatne i są przeznaczone do użycia wyłącznie z A - z

shape.RECTANGLE; 

Należy zauważyć, że to rozwiązanie nadal cierpi z powodu problemu z inicjalizacją zmiennych statycznych . Gdy wartość statyczna zostanie użyta do zainicjowania innej zmiennej statycznej, , pierwsza może nie zostać zainicjowana, .

// file.h 
class File { 
public: 
    static struct _Extensions { 
    const std::string h{ ".h" }; 
    const std::string hpp{ ".hpp" }; 
    const std::string c{ ".c" }; 
    const std::string cpp{ ".cpp" }; 
    } extension; 
}; 

// file.cpp 
File::_Extensions File::extension; 

// module.cpp 
static std::set<std::string> headers{ File::extension.h, File::extension.hpp }; 

W tym przypadku zmienna statyczna nagłówki zawiera zarówno { „”} lub { „.h”, „.hpp”}, w zależności od celu inicjalizacji utworzonego przez łącznik.

Jak wspomniano w @ abyss.7, można również użyć wartości constexpr, jeśli wartość zmiennej można obliczyć w czasie kompilacji. Ale jeśli deklarują swoje struny static constexpr const char* i program wykorzystuje std::string inaczej nie będzie nad głową, ponieważ nowe std::string obiekt będzie tworzone za każdym razem, gdy używasz takiego Constant:

class A { 
public: 
    static constexpr const char* STRING = "some value"; 
}; 
void foo(const std::string& bar); 
int main() { 
    foo(A::STRING); // a new std::string is constructed and destroyed. 
} 
1

w C++ 17 można użyć inline zmienne:

class A { 
private: 
    static inline const std::string my_string = "some useful string constant"; 
}; 

pamiętać, że jest inna od abyss.7's answer: to określa rzeczywistą std::string obiektu, a nie const char*

Powiązane problemy