Sé que podemos llamar explícitamente al constructor de una clase en C++ utilizando el operador de resolución de ámbito, es decir, className::className()
. Me preguntaba dónde exactamente necesitaría hacer tal llamada.Por qué llamar explícitamente a un constructor en C++
Respuesta
mayoría de las veces, en un constructor de la clase infantil que requieren algunos parámetros:
class BaseClass
{
public:
BaseClass(const std::string& name) : m_name(name) { }
const std::string& getName() const { return m_name; }
private:
const std::string m_name;
//...
};
class DerivedClass : public BaseClass
{
public:
DerivedClass(const std::string& name) : BaseClass(name) { }
// ...
};
class TestClass :
{
public:
TestClass(int testValue); //...
};
class UniqueTestClass
: public BaseClass
, public TestClass
{
public:
UniqueTestClass()
: BaseClass("UniqueTest")
, TestClass(42)
{ }
// ...
};
... por ejemplo.
Aparte de eso, no veo la utilidad. Solo llamé al constructor en otro código cuando era demasiado pequeño para saber lo que realmente estaba haciendo ...
C++ llama implícitamente al constructor de las clases principales cuando se construye una instancia de una clase derivada, pero llama al constructor predeterminado, a menos que explícitamente invoque un constructor de clases padre específico en la lista de inicializadores. –
Sí, en mi ejemplo, me aseguré de que el único constructor válido para BaseClass requiriera algunos parámetros. No recuerdo un caso que requiera una llamada de constructor predeterminada explícita. Tal vez en herencia virtual? – Klaim
No creo que normalmente usarías eso para el constructor, al menos no de la manera que estás describiendo. Sin embargo, lo necesitarías si tienes dos clases en espacios de nombres diferentes. Por ejemplo, para especificar la diferencia entre estas dos clases inventadas, Xml::Element
y Chemistry::Element
.
Normalmente, el nombre de la clase se utiliza con el operador de resolución de alcance para llamar a una función en el elemento primario heredado de una clase. Entonces, si tiene un perro de clase que hereda de Animal, y ambas clases definen la función Eat() de manera diferente, puede haber un caso en el que desee usar la versión Animal de comer en un objeto Dog llamado "someDog". Mi sintaxis de C++ está un poco oxidada, pero creo que en ese caso dirías someDog.Animal::Eat()
.
También a veces se usa explícitamente un constructor para crear un temporal. Por ejemplo, si tiene alguna clase con un constructor:
class Foo
{
Foo(char* c, int i);
};
y una función
void Bar(Foo foo);
pero no tienen un Foo alrededor, que podría hacer
Bar(Foo("hello", 5));
Este es como un yeso. De hecho, si tiene un constructor que toma solo un parámetro, el compilador C++ usará ese constructor para realizar moldes implícitos.
Es no legal para llamar a un constructor en un objeto ya existente. Es decir, no puede hacer
Foo foo;
foo.Foo(); // compile error!
haga lo que haga. Pero puede invocar un constructor sin asignar memoria; para eso es ubicación nueva.
char buffer[sizeof(Foo)]; // a bit of memory
Foo* foo = new(buffer) Foo(); // construct a Foo inside buffer
Da nueva memoria y construye el objeto en ese lugar en lugar de asignar nueva memoria. Este uso se considera malo, y es raro en la mayoría de los tipos de código, pero es común en los códigos incrustados y de estructura de datos. Por ejemplo, std::vector::push_back
usa esta técnica para invocar el constructor de copia. De esta forma, solo necesita hacer una copia, en lugar de crear un objeto vacío y usar el operador de asignación.
+1 para la colocación nueva. Es extraño, pero puede ser útil si sabes lo que estás haciendo. –
"De hecho, si tiene un constructor que toma solo un parámetro, el compilador de C++ usará ese constructor para realizar conversiones implícitas". - por lo que muchas personas colocan la palabra clave explícita en constructores de una sola arg por defecto, y solo la quitan si están seguros de que quieren implict casting del tipo de parámetro al tipo de clase. –
-1 No estás llamando al constructor. La sintaxis es '
Creo que el mensaje de error de error del compilador C2585 da la mejor razón por la que tendría que utilizar realmente el operador alcance resolución en el constructor, y lo hace con la respuesta de Charlie:
conversión de una clase o tipo de estructura basada en herencia múltiple. Si el tipo hereda la misma clase base más de una vez, la función de conversión u operador debe usar la resolución del ámbito (: :) para especificar cuál de las clases heredadas usar en la conversión.
Imagine que tiene BaseClass, y BaseClassA y BaseClassB ambos heredan BaseClass, y luego DerivedClass hereda tanto BaseClassA como BaseClassB.
Si está realizando una conversión o sobrecarga del operador para convertir DerivedClass a BaseClassA o BaseClassB, necesitará identificar qué constructor (creo que algo así como un constructor de copia, IIRC) usar en la conversión.
En general, no llama al constructor directamente. El nuevo operador lo llama por usted o una subclase llama a los constructores de la clase padre. En C++, se garantiza que la clase base estará completamente construida antes de que comience el constructor de la clase derivada.
La única vez que llamaría directamente a un constructor es en el caso extremadamente raro en el que está administrando memoria sin usar nueva. E incluso entonces, no deberías hacerlo. En su lugar, debe usar la forma de ubicación del operador nuevo.
Existen casos de uso válidos en los que desea exponer un constructor de clases. Si desea hacer su propia gestión de memoria con un asignador de arena, por ejemplo, necesitará una construcción de dos fases que consista en la asignación y la inicialización de objetos.
El enfoque que tomo es similar al de muchos otros idiomas. Simplemente puse mi código de construcción en métodos públicos bien conocidos (Construct(), init(), algo así) y los llamo directamente cuando sea necesario.
Puede crear sobrecargas de estos métodos que coinciden con sus constructores; tus constructores regulares solo llaman a ellos. Ponga grandes comentarios en el código para advertir a los demás que está haciendo esto para que no agreguen código de construcción importante en el lugar equivocado.
Recuerde que solo hay un método destructor sin importar qué sobrecarga de construcción se utilizó, por lo que convierta sus destructores en robustos para los miembros no inicializados.
No recomiendo intentar escribir inicializadores que puedan volver a inicializarse. Es difícil decir en el caso en el que está mirando un objeto que solo tiene basura debido a la memoria no inicializada que a la retención de datos reales.
El problema más difícil se presenta con las clases que tienen métodos virtuales. En este caso, el compilador normalmente conecta el puntero de la tabla de funciones vtable como un campo oculto al inicio de la clase. Puede inicializar manualmente este puntero, pero básicamente depende del comportamiento específico del compilador y es probable que sus colegas lo vean divertido.
La ubicación nueva se ha roto en muchos aspectos; en la construcción/destrucción de matrices es un caso, así que tiendo a no usarlo.
Considere el siguiente programa.
template<class T>
double GetAverage(T tArray[], int nElements)
{
T tSum = T(); // tSum = 0
for (int nIndex = 0; nIndex < nElements; ++nIndex)
{
tSum += tArray[nIndex];
}
// Whatever type of T is, convert to double
return double(tSum)/nElements;
}
Esto llamará explícitamente a un constructor predeterminado para inicializar la variable.
- 1. Llamar explícitamente al constructor estático
- 2. ¿Cuándo necesita llamar explícitamente a un constructor de superclase?
- 3. ¿Llamar a System.gc() explícitamente?
- 4. ¿Por qué llamar a super() en un constructor?
- 5. ¿Puede un constructor llamar a otro constructor en C++?
- 6. ¿Por qué no puedo hacer referencia a un método de instancia mientras invoco explícitamente un constructor?
- 7. explícitamente incumplido movimiento constructor
- 8. Llamar a un método en Constructor
- 9. En C#, ¿necesita llamar al constructor base?
- 10. ¿Por qué este código intenta llamar al constructor de copia?
- 11. Utilizando C# reflexión para llamar a un constructor
- 12. ¿Por qué es necesario llamar explícitamente a Myclass :: operator string() con std :: string :: operator +()?
- 13. C# llamar a un constructor del cuerpo de otro
- 14. ¿Por qué un puntero inteligente no puede llamar a new() para mí en su constructor?
- 15. ¿Por qué escribir explícitamente "privado"?
- 16. ¿Por qué mi subclase de C++ necesita un constructor explícito?
- 17. ¿Por qué los métodos virtuales deben anularse explícitamente en C#?
- 18. ¿Puedo llamar a un constructor sobrecargado de otro constructor de la misma clase en C#?
- 19. Llamar a un constructor parametrizado de XAML
- 20. ¿Tengo que llamar explícitamente a System.exit() en una aplicación Webstart?
- 21. ¿Cómo llamar al constructor protegido en C#?
- 22. ¿Por qué no puedo llamar a métodos dentro de una clase que implementa explícitamente una interfaz?
- 23. ¿Por qué puede llamar a un constructor de copia que pasa en el objeto que está construyendo? (C++) (gcc)
- 24. Llamar a un método reemplazado de un constructor
- 25. ¿Por qué puedo llamar al constructor privado desde? Ámbito global?
- 26. Llamar por programación a webmethods en C#
- 27. ¿Por qué llamar a IsDebugEnabled en log4net?
- 28. ¿Llamar explícitamente a base() en constructores derivados es opcional?
- 29. ¿Por qué llamar a setResult en BroadcastReceiver?
- 30. ¿Por qué debería rodear explícitamente con "desmarcado"?
No es correcto decir que puede llamar al constructor directamente. El estándar tiene explícitamente (12.1/1): "Los constructores no tienen nombres". Solo puede llamar al constructor a través de otras construcciones, como un molde de estilo de función o una ubicación nueva. –