programa lenguaje headers ejemplos dev crear cabeceras cabecera archivos c++ header-files

headers - ejemplos de archivos de cabecera en lenguaje c++



definición múltiple en el archivo de cabecera (4)

Dado este ejemplo de código:

complex.h:

#ifndef COMPLEX_H #define COMPLEX_H #include <iostream> class Complex { public: Complex(float Real, float Imaginary); float real() const { return m_Real; }; private: friend std::ostream& operator<<(std::ostream& o, const Complex& Cplx); float m_Real; float m_Imaginary; }; std::ostream& operator<<(std::ostream& o, const Complex& Cplx) { return o << Cplx.m_Real << " i" << Cplx.m_Imaginary; } #endif // COMPLEX_H

complex.cpp:

#include "complex.h" Complex::Complex(float Real, float Imaginary) { m_Real = Real; m_Imaginary = Imaginary; }

main.cpp:

#include "complex.h" #include <iostream> int main() { Complex Foo(3.4, 4.5); std::cout << Foo << "/n"; return 0; }

Al compilar este código, obtengo el siguiente error:

multiple definition of operator<<(std::ostream&, Complex const&)

Descubrí que hacer esta función en inline resuelve el problema, pero no entiendo por qué. ¿Por qué el compilador se queja de la definición múltiple? Mi archivo de encabezado está protegido (con #define COMPLEX_H ).

Y, si se queja de la función del operator<< , ¿por qué no se queja de la función public real() , que también se define en el encabezado?

¿Y hay otra solución además de usar la palabra clave en inline ?


¿Y hay otra solución como usar la palabra clave en inline ?

Sí hay. Además de definir el método dentro del archivo de implementación complex.cpp como lo mencionaron otros, también puede colocar la definición en un espacio de nombre sin nombre.

namespace { std::ostream& operator<<(std::ostream& o, const Complex& Cplx) { return o << Cplx.m_Real << " i" << Cplx.m_Imaginary; } }

En la práctica, esto creará un espacio de nombres único para cada unidad de compilación. De esa manera, evitas los choques de nombre. Sin embargo, los nombres aún se exportan desde la unidad de compilación pero son inútiles (ya que los nombres son desconocidos).

Poner la definición dentro del archivo de implementación es a menudo una mejor solución. Sin embargo, para las plantillas de clase no puede hacerlo ya que los compiladores de C ++ no son compatibles con las plantillas de creación de instancias en una unidad de compilación diferente a la que se definieron. En ese caso, debe usar un espacio de nombres en inline o sin nombre.


El problema es que el siguiente fragmento de código es una definición, no una declaración:

std::ostream& operator<<(std::ostream& o, const Complex& Cplx) { return o << Cplx.m_Real << " i" << Cplx.m_Imaginary; }

Puede marcar la función de arriba y hacerla "en línea" para que múltiples unidades de traducción puedan definirla:

inline std::ostream& operator<<(std::ostream& o, const Complex& Cplx) { return o << Cplx.m_Real << " i" << Cplx.m_Imaginary; }

O simplemente puede mover la definición original de la función al archivo fuente "complex.cpp".

El compilador no se queja de "real ()" porque está implícitamente en línea (cualquier función miembro cuyo cuerpo aparece en la declaración de clase se interpreta como si hubiera sido declarada "en línea"). Las guardias del preprocesador impiden que su encabezado se incluya más de una vez desde una sola unidad de traducción ("* .cpp" archivo de origen "). Sin embargo, ambas unidades de traducción ven el mismo archivo de encabezado. Básicamente, el compilador compila" main.cpp "para "main.o" (incluidas las definiciones que figuran en los encabezados incluidos en "main.cpp"), y el compilador compila por separado "complex.cpp" a "complex.o" (incluidas las definiciones que figuran en los encabezados incluidos en "complex .cpp "). Luego el enlazador combina" main.o "y" complex.o "en un solo archivo binario; es en este punto que el enlazador encuentra dos definiciones para una función del mismo nombre. indica que el vinculador intenta resolver referencias externas (por ejemplo, "main.o" se refiere a "Complex :: Complex" pero no tiene una definición para esa función ... el vinculador localiza la definición de "complex.o" y resuelve esa referencia).


Estaba teniendo este problema, incluso después de que mi fuente y el archivo de encabezado fueran correctos.

Resultó que Eclipse estaba usando artefactos obsoletos de una compilación anterior (fallida).

Para corregirlo, usa Project > Clean luego reconstruye.


Mueve la implementación a complex.cpp

En este momento, después de incluir este archivo, se está compilando la implementación de cada archivo. Más adelante, durante la vinculación, hay un conflicto evidente debido a implementaciones duplicadas.

:: real () no se informa porque está en línea implícitamente (implementación dentro de la definición de clase)