2009-10-14 21 views
354

Me gustaría tener una constante estática privada para una clase (en este caso, una fábrica de formas). Me gustaría tener algo por el estilo.Cadena estática constante de C++ (miembro de la clase)

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

Por desgracia, conseguir todo tipo de errores de la C++ (++ g) del compilador, tales como:

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

Esto me dice que este tipo de diseño de miembro no es compatible con el estándar. ¿Cómo se tiene una constante literal privada (o quizás pública) sin tener que usar una directiva #define (quiero evitar la fealdad de la información globalizada)?

Se agradece cualquier ayuda. Gracias.

+11

¡Gracias por todas sus excelentes respuestas! ¡Viva SO! –

+0

¿Puede alguien decirme qué tipo 'integral' es? Muchas gracias. –

+1

Los tipos integrales se refieren a los tipos que representan números enteros. Consulte http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fintvar.htm – bleater

Respuesta

377

Debe definir su miembro estático fuera de la definición de clase y proporcionar el inicializador allí.

Primera

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

y luego

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

La sintaxis que fueron originalmente intentando utilizar (inicializador dentro de la definición de clase) sólo se permite con tipos integrales y de enumeración.

+5

Además, si no hay un requisito para usar una cadena STL, también podría definir un const char *. (menos gastos generales) – KSchmidt

+42

No estoy seguro de que siempre haya menos gastos generales, esto depende del uso. Si este miembro se debe pasar como un argumento para las funciones que toman const string &, se creará temporalmente para cada llamada frente a una creación de objeto de cadena durante la inicialización. En mi humilde opinión sobre la creación de un objeto de cadena estática es insignificante. –

+17

Prefiero usar std :: string todo el tiempo también. La sobrecarga es insignificante, pero tiene muchas más opciones y es mucho menos probable que escriba cosas tontas como "magia" == A :: RECTÁNGULO solo para comparar su dirección ... –

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.

Esta es la restricción. Por lo tanto, en este caso necesita definir una variable fuera de la clase. consulte answwer de @AndreyT

15

Esta es sólo información adicional, pero si realmente desea la cadena en un fichero de cabecera, intente algo como:

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

     return str; 
    } 
}; 

Aunque dudo que recomienda.

+0

Eso se ve genial :) - ¿Supongo que tienes antecedentes en otros idiomas además de C++? –

+4

No lo recomendaría. Lo hago con frecuencia Funciona bien y me resulta más obvio que poner la cadena en el archivo de implementación. Sin embargo, los datos reales de std :: string aún se encuentran en el montón. Devolvería un const char *, en cuyo caso no es necesario declarar la variable estática para que la declaración ocupe menos espacio (en lo que respecta al código). Solo una cuestión de gusto. – Zoomulator

4

El estándar actual solo permite tal inicialización para tipos integrales de constante estática. Entonces debes hacer lo que AndreyT explicó. Sin embargo, eso estará disponible en el siguiente estándar a través del new member initialization syntax.

30

Dentro de las definiciones de clase solo puede declarar miembros estáticos. Deben ser definidos fuera de la clase. Para las constantes integrales en tiempo de compilación, el estándar establece la excepción de que puede "inicializar" miembros. Sin embargo, todavía no es una definición. Tomar la dirección no funcionaría sin definición, por ejemplo.

Me gustaría mencionar que no veo el beneficio de usar std :: string sobre const char [] para las constantes. std :: string es agradable y todo requiere una inicialización dinámica. Por lo tanto, si se escribe algo así como

const std::string foo = "hello"; 

en el ámbito del espacio de nombres al constructor de foo se llevará a cabo justo antes de la ejecución de arranques principales y este constructor va a crear una copia de la constante "hola" en la memoria del montón. A menos que realmente necesita rectángulo para ser un std :: string usted podría escribir así

// 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"; 

No! Sin asignación de montón, sin copia, sin inicialización dinámica.

Saludos, s.

5

posible simplemente hacer:

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

o

#define RECTANGLE "rectangle" 
+7

El uso de #define cuando se puede usar una constante mecanografiada es simplemente incorrecto. –

+0

Su primer ejemplo es básicamente una buena solución si no tiene 'constexpr' pero no puede hacer una función estática' const'. –

+0

Esta solución debe evitarse. Crea una nueva cadena en cada invocación. Esto sería mejor: 'static const std :: string RECTANGLE() const {static const std :: string value (" rectangle "); valor de retorno; } ' –

4

Usted puede ir para la solución const char* mencionado anteriormente, pero entonces si necesita cadena de todo el tiempo, vas a tener un montón de sobrecarga
Por otro lado, la cadena estática necesita inicialización dinámica, por lo tanto, si desea utilizar su valor durante la inicialización de otra variable global/estática, puede solucionar el problema del orden de inicialización. Para evitar eso, lo más barato es acceder al objeto de cadena estática a través de un getter, que verifica si su objeto está inicializado o no.

//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; 
} 

Recuerde utilizar solo A::getS(). Como cualquier subproceso solo puede iniciarse con main(), y A_s_initialized se inicializa antes de main(), no necesita bloqueos incluso en un entorno multiproceso. A_s_initialized es 0 por defecto (antes de la inicialización dinámica), por lo que si usa getS() antes de que s se inicialice, llama a la función init de forma segura.

Por cierto, en la respuesta anterior: "estática const RECTANGULAR std :: string() const", funciones estáticas pueden no ser const porque no pueden cambiar el estado si cualquier objeto de todos modos (no hay este puntero).

113

En C++ 11 que puede hacer ahora: las variables estáticas

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

Gracias, eso salvó el día. Necesitaba un conjunto de bits de tamaño de una matriz estática en mi clase. No es posible sin constexpr (para el conjunto) a menos que desee la asignación del montón cada vez que se crea la clase. – rwst

+15

Desafortunadamente esta solución no funciona para std :: string. – SmallChess

+0

Tenga en cuenta que 1. esto solo funciona con literales y 2. esto no es estándar, aunque Gnu/GCC compila multas, otros compiladores arrojarán un error. La definición tiene que estar en el cuerpo. – ManuelSchneid3r

3

la clase puede ser declarado en la cabecera, sino que debe ser definido en un archivo .cpp. Esto se debe a que solo puede haber una instancia de una variable estática y el compilador no puede decidir en qué archivo de objeto generado se va a ubicar, por lo que debe tomar la decisión.

Para mantener la definición de un valor estático con la declaración en C++ 11 se puede utilizar una estructura estática anidada. En este caso, el miembro estático es una estructura y debe definirse en un archivo .cpp, pero los valores están en el encabezado.

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

En lugar de inicializar los miembros individuales de toda la estructura estática se inicializa en .cpp:

A::_Shapes A::shape; 

Los valores se accede con

A::shape.RECTANGLE; 

o - ya que los miembros son privados y están destinados a ser utilizados solo desde A - con

shape.RECTANGLE; 

Tenga en cuenta que esta solución aún adolece del problema del orden de inicialización de las variables estáticas . Cuando se utiliza un valor estático en inicialice otra variable estática, la primera puede no inicializarse, todavía.

// 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 }; 

En este caso las variables estáticas cabeceras contendrán ya sea { ""} o { ".h", ".hpp"}, dependiendo del orden de inicialización creado por el enlazador.

Según lo mencionado por @ abyss.7 también puede usar constexpr si el valor de la variable se puede calcular en tiempo de compilación. Pero si declara sus cuerdas con static constexpr const char* y el programa utiliza std::string de lo contrario habrá una sobrecarga porque se creará un nuevo std::string objeto cada vez que se utiliza una constante tal:

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

En C++ 17 puede utilizar en línea variables de:

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

Tenga en cuenta que esto es diferente de abyss.7's answer: éste define un std::string objeto real, no un const char*

Cuestiones relacionadas