una resueltos resolucion programacion orientada operador objetos miembros ejercicios ejemplos codigo clases clase ambito c++ string class static const

resueltos - Cadena constante estática de C++(miembro de clase)



operador de resolucion de ambito c++ (11)

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.

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

Desafortunadamente, recibo todo tipo de errores del compilador de C ++ (g ++), como:

ISO C ++ prohíbe la inicialización del miembro ''RECTANGLE''

inicialización no válida dentro de la clase del miembro de datos estáticos del tipo no integral ''std :: string''

error: haciendo ''RECTANGLE'' estático

Esto me dice que este tipo de diseño de miembro no cumple 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 globalidad de los datos)?

Cualquier ayuda es apreciada. Gracias.


Para usar esa sintaxis de inicialización en clase, la constante debe ser una constante estática de integral o tipo de enumeración inicializado por una expresión constante.

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


Avance rápido a 2018 y C ++ 17.

using namespace std::literals; namespace STANDARD { constexpr inline auto compiletime_static_string_view_constant() { // make and return string view literal // will stay the same for the whole application lifetime // will exhibit standard and expected interface // will be usable at both // runtime and compile time // by value semantics implemented for you auto made_once_when_needed_ = "compile time"sv; return made_once_when_needed_ ; } };

Lo anterior es un ciudadano propio de C ++ estándar y legal. Puede involucrarse fácilmente en cualquiera y todos los algoritmos estándar, contenedores, utilidades y demás. Por ejemplo:

// test the resilience auto return_by_val = []() { auto return_by_val = []() { auto return_by_val = []() { auto return_by_val = []() { return STANDARD::compiletime_static_string_view_constant(); }; return return_by_val(); }; return return_by_val(); }; return return_by_val(); }; // actually a run time _ASSERTE(return_by_val() == "compile time"); // compile time static_assert( STANDARD::compiletime_static_string_view_constant() == "compile time" );

Disfruta del estándar C ++


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

primero

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

y entonces

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

La sintaxis que originalmente intentaba usar (inicializador dentro de la definición de clase) solo se permite con tipos integrales y enumeración.


Dentro de las definiciones de clase solo puedes declarar miembros estáticos. Tienen que ser definidos fuera de la clase. Para las constantes integrales en tiempo de compilación, el estándar hace la excepción de que puede "inicializar" miembros. Aunque 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 over const char [] para las constantes . std :: string es agradable y todo, pero requiere una inicialización dinámica. Entonces, si escribes algo como

const std::string foo = "hello";

en el ámbito del espacio de nombres, el constructor de foo se ejecutará justo antes de la ejecución de los inicios principales y este constructor creará una copia de la constante "hola" en la memoria del montón. A menos que realmente necesites RECTANGLE para ser una cadena estándar :: podrías escribir.

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

¡Ahí! Sin asignación de almacenamiento dinámico, sin copia, sin inicialización dinámica.

Saludos, s.


El estándar actual solo permite dicha inicialización para tipos integrales constantes estáticos. Así que tienes que hacer lo que AndreyT explicó. Sin embargo, eso estará disponible en el próximo estándar a través de la nueva sintaxis de inicialización de miembros .


En C ++ 11 puedes hacerlo ahora:

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


En C ++ 17 puedes usar variables en línea :

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

Tenga en cuenta que esto es diferente de la respuesta de abyss.7 : Esta define un objeto std::string real, no un const char*


Esto es solo información adicional, pero si realmente desea la cadena en un archivo de encabezado, intente algo como:

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

Aunque dudo que sea recomendado.


Las variables estáticas de clase pueden declararse en el encabezado, pero deben definirse 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 colocarlo, 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 usar 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 miembros individuales, toda la estructura estática se inicializa en .cpp:

A::_Shapes A::shape;

Se accede a los valores con

A::shape.RECTANGLE;

o - dado 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 sufre el problema del orden de inicialización de las variables estáticas. Cuando se usa un valor estático para inicializar otra variable estática, es posible que el primero aún no se haya inicializado.

// 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, los encabezados de variables estáticas contendrán {""} o {".h", ".hpp"}, según el orden de inicialización creado por el enlazador.

Como lo mencionó @ abyss.7, también podría usar constexpr si el valor de la variable se puede calcular en el momento de la compilación. Pero si declara sus cadenas con static constexpr const char* y su programa usa std::string contrario habrá una sobrecarga porque se creará un nuevo objeto std::string cada vez que use una constante de este tipo:

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. }


Puede optar por la solución const char* mencionada anteriormente, pero si necesita una cadena todo el tiempo, tendrá muchos gastos generales.
Por otro lado, la cadena estática necesita una inicialización dinámica, por lo tanto, si desea utilizar su valor durante la inicialización de otra variable global / estática, podría tener un problema de 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; }

Recuerda usar solo A::getS() . Debido a que cualquier subproceso solo puede iniciarse por main() , y A_s_initialized se inicializa antes que 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 se inicialice s, puede llamar a la función init de forma segura.

Por cierto, en la respuesta anterior: " static const std :: string RECTANGLE () const ", las funciones estáticas no pueden ser const porque no pueden cambiar el estado de ningún objeto (no existe este puntero).


posible solo hacer

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

o

#define RECTANGLE "rectangle"