我正在尝试重载<<
类的ostream
运算符?
出于某种原因,我将它重载了两次,我似乎无法弄清楚为什么我的头文件中有#ifndef
。
matrix.h
#ifndef MATRIX_H
#define MATRIX_H
#include <iostream>
using namespace std;
class matrix {
int x, y;
public:
matrix(int a, int b);
matrix& operator* (matrix B);
friend ostream& operator<< (ostream& os, const matrix& A);
};
ostream& operator<< (ostream& os, const matrix& A)
{
os << "Matrix.....";
return os;
}
#endif
matrix.cpp
#include <iostream>
#include "matrix.h"
matrix::matrix(int a, int b) {
}
matrix& matrix::operator* (matrix B) {
}
和 main.cpp
#include <iostream>
#include "matrix.h"
using namespace std;
int main () {
matrix a(6, 6), b(6, 6);
cout << a;
return 0;
}
我正在这样建设:
$ cat build.sh
g++ -c main.cpp
g++ -c matrix.cpp
g++ -g -o main main.o matrix.o
我得到的构建错误是:
$bash build.sh
ld: duplicate symbol operator<<(std::basic_ostream<char, std::char_traits<char> >&, matrix const&)in matrix.o and main.o for architecture x86_64
collect2: ld returned 1 exit status
认为这是前进的,但我似乎无法找到解决方案。
感谢您的时间。
g ++ -v
$g++ -v
...skipped 4 lines...
gcc version 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.1.00)
答案 0 :(得分:9)
您正在定义具有外部链接的函数,并将其包含在两个编译单元中。
我主张只在函数定义中添加inline
关键字,或者在类中移动函数定义。
通过添加与static
类似的非operator<<
免费函数,可以定义具有外部链接的函数,这意味着它会在生成的目标文件的符号表中生成一个条目。
如果函数是inline
或template
,则编译器/链接器必须处理此问题,例如只需挑选任意一个。 (这没关系,因为ODR会使它们等效。)
但是,如果不是这种情况(如在您的示例中),则会导致链接器错误,因为链接器不知道您的意思。
您还可以(或者甚至另外)将您的函数声明为static
,这会导致它失去外部链接(取而代之的是获取内部链接),这只是一种说它不会生成的奇特方式符号表条目。这将导致此函数被编译为包含标题的每个编译单元的新版本,因此不如其他解决方案。
仅阻止在一个编译单元中多次定义函数。
首先,您可以利用友元函数的属性并在类中定义它(这有效地使它获得C ++ 11.3 / 7中定义的inline
关键字):
class matrix {
int x, y;
public:
matrix(int a, int b);
matrix& operator* (matrix B);
friend ostream& operator<< (ostream& os, const matrix& A)
{
os << "Matrix.....";
return os;
}
};
或者,您可以通过在其前面添加inline
来使其成为inline
功能。
您有时可能会看到这两种技术相结合:在类中定义并使用inline
注释的友元函数。在这种情况下,inline
完全是多余的,并且不会改变任何内容。
您也可以通过向其添加static
关键字或将其包含在unnamed namespace内来使其获得内部链接。虽然这可以解决问题,但它会导致不必要的膨胀,因为包含此函数的每个编译单元都有自己的内部副本。技术上,内部链接可以与inline
结合使用。
在技术上可行的情况下,尝试制作模板功能是很多工作,并没有真正获得超越其他解决方案的任何东西。但会解决您的问题,导致变体非常类似于inline
版本。
作为最后一个选项,您可以将函数定义移动到单个编译单元中(读取:.cpp文件)。 matrix.cpp文件肯定是志愿者,因为它保留了与matrix
类相关的功能。