重载模板类上的<<运算符
Overloading << operator on template classes
我为链表中的节点创建了一个模板类,并试图通过重载<lt;。然而,我目前的代码:
#include <iostream>
using namespace std;
template<class NType> class Node;
template<class NType>
class Node {
private:
void deletePointer(NType* p);
public:
NType data;
Node *prev, *next;
template<typename T>
struct is_pointer { static const bool value = false; };
template<typename T>
struct is_pointer<T*> { static const bool value = true; };
Node();
Node(NType data);
~Node();
};
int main() {
Node<int> *n1 = new Node<int>();
Node<int> *n2 = new Node<int>(10);
std::cout << "Node 1: " << n1 << std::endl;
std::cout << "Node 2: " << n2 << std::endl;
}
template<class NType> inline std::ostream & operator << (std::ostream& out, const Node<NType> &node){
out << node.data;
return out;
}
template<class NType> inline Node<NType>::Node()
:data(NULL), prev(NULL), next(NULL)
{
}
template<class NType> inline Node<NType>::Node(NType data)
:data(data), prev(NULL), next(NULL)
{
}
template<class NType> inline Node<NType>::~Node(){
if(is_pointer<NType>::value){
deletePointer(&data);
} else {
return;
}
}
template<class NType> inline void Node<NType>::deletePointer(NType* p){
delete p;
}
输出内存位置,而不是节点内的数据。这种情况发生在诸如int
之类的基元类型上,就好像它不知道NType
容器中有什么类型的数据一样。
Node 1: 0x741010
Node 2: 0x741030
Node 3: 0x741070
Node 4: 0x741090
我试过使用typename
而不是class
,但仍然没有骰子。。。在插入之前,是否有任何方法可以动态地找出模板正在使用和转换的类型?我知道我可以为所有基元生成大量冗余代码,但这似乎是浪费和不必要的。
如果有帮助的话,我正在使用GCC v4.6.2 20111223 在Arch Linux x64上编译
编辑:因为很多人都提到了它。我也尝试过把类作为朋友和独立函数放在外面,这两种功能都不起作用,因为无论我把它放在哪里,流都会输出地址,而不是数据本身。没有可访问的私有数据值,所以它可以不作为朋友。
编辑:测试用例:http://ideone.com/a99u5还更新了上面的来源。
编辑:添加了我代码的剩余部分,以帮助Aaron理解代码。
您的代码将operator<<
声明为成员函数,因此它实际上将this
指针作为第一个参数,ostream
作为第二个参数。相反,它需要是一个免费的功能:
template<class NType> class Node {
public:
NType data;
Node *prev, *next;
};
//Note how this is declared outside of the class body, so it is a free function instead of a memberfunction
template<class NType> inline std::ostream& operator<<(std::ostream& out, const Node<NType>& val){
out << val.data;
return out;
}
但是,如果您的operator<<
需要访问私人数据,则需要将其声明为朋友函数:
template<class NType> class Node {
public:
NType data;
Node *prev, *next;
friend std::ostream& operator<<(std::ostream& out, const Node& val){
out << val.data;
return out;
}
};
现在是您的输出:如果调用了operator<<
,编译器将知道NType
的类型,并在流式传输data
成员时做正确的事情。然而,由于您的operator<<
不应该工作(如所写),并且它似乎会将内存地址作为输出,我认为您有以下内容:
Node* n = new Node();
std::cout<<n;
//When it should be:
std::cout<<*n;
现在只是为了好奇:为什么你自己实现看起来奇怪的链表,而不是简单地使用std::list
?
编辑:现在我们可以看到测试用例了,关于如何调用operator<<
的假设似乎是正确的。输出需要更改为:
std::cout << "Node 1: " << *n1 << std::endl;
std::cout << "Node 2: " << *n2 << std::endl;
为Node
实际调用operator<<
,而不是为T*
调用通用的
为什么要在已经模板化的类方法前面添加template<class NType>
?
一般来说,重载运算符<lt;就是让它成为朋友:
template<typename NType>
friend std::ostream& operator<<(std::ostream& out, const Node<NType>& node)
{
out << node.data;
return out;
}
编辑:为了回应下面的评论,这就是我的意思:
当您在头中定义一个类模板时,您不会为类的成员函数重新声明该模板:
template<typename T>
class Foo
{
T some_data;
void member_fn();
}
然而,当您声明非成员朋友函数时,这是必需的:
template<typename NType>
class Node
{
public:
NType data;
Node<NType> *prev, *next;
//Note the different template parameter!
template<typename NType1>
friend std::ostream& operator<<(std::ostream& out, const Node<NType1>& node);
};
然后,它的实现就变成了上面的template<typename NType> std::ostream& operator<<(std::ostream& out, const Node<NType>& node)
实现。
template <class NType>
class Node
{
public:
NType data;
Node *prev, *next;
};
template <class NType>
inline std::ostream& operator<<(std::ostream& os, const Node<NType>& n)
{
return out << n.data;
}
您打印的是节点地址,而不是节点:
int main() {
Node<int> *n1 = new Node<int>();
Node<int> *n2 = new Node<int>(10);
std::cout << "Node 1: " << n1 << std::endl;
std::cout << "Node 2: " << n2 << std::endl;
}
n1
和n2
是指针,而不是对象。你应该写:
int main() {
Node<int> *n1 = new Node<int>();
Node<int> *n2 = new Node<int>(10);
std::cout << "Node 1: " << *n1 << std::endl;
std::cout << "Node 2: " << *n2 << std::endl;
}
或:
int main() {
Node<int> n1();
Node<int> n2(10);
std::cout << "Node 1: " << n1 << std::endl;
std::cout << "Node 2: " << n2 << std::endl;
}
其他人指出您需要使用cout << *n1
而不是cout << n1
,但您的代码中还有另一个重要的错误。
析构函数包含一个调用deletePointer(&data);
,其中data
是类的成员。这很危险。如果执行了这行代码,那么程序可能会崩溃。data
只是this
指向的对象的一小部分,试图删除它就像试图删除数组中的单个元素是int。
Node<int> *n1 = new Node<int>();
delete n1; // this makes sense. deleting the same thing that was new'ed
Node<int> *n1 = new Node<int>();
delete &(n1->data); // BUG
您可能应该对设计进行大量更改,并简单地删除指针的特殊代码。您希望编写这样的代码吗:Node<int*> *n2 = new Node<int*>( new int );
?如果是这样,你希望它如何表现?
如果NType
实际上是一个指针类型,就像int*
一样,那么做deletePointer(data)
可能是有意义的,但做deletePointer(&data)
永远没有意义。
- 如何防止clang格式在流运算符调用之间添加换行符<<
- 如何显式调用运算符<<
- 为什么COUT在朋友函数中不起作用,该功能超载了操作员&lt;&lt;这是一个iStream运算符
- C++运算符<<调用::ostream而不是std::osttream
- BOOST ::变体无法解决运算符&lt;&lt;对于STD :: Ostream
- 过载输出<<用于类的运算符,以打印其中的元组
- C++ostream:没有运算符匹配<<&应在'&'代币
- 重载运算符<<:此运算符函数的参数太多
- C++继承运算符<<
- 重载运算符<<用于模板类.即使使用好友关键字也无法获得私人会员
- 如何过载<<用于YAML::Emitter的运算符,以序列化包含另一个自定义类的向量的自定义类
- 为什么字符串流运算符<<擦除原始值
- 关于使用运算符<<为新手提供C++中的模板
- 我已经完成了<<运算符重载,但它'It’不起作用
- 重载运算符<<输出地址而不是数据成员
- 错误:没有匹配'运算符<<"在'std::cout
- 重载运算符<<用于ostream语法
- 当运算符<存在时,为什么要定义 LT?
- log4cxx访问异常,使用<<运算符和宏
- 重载<<运算符错误C2804:二进制'运算符<<'参数太多