【发布时间】:2021-03-15 13:45:53
【问题描述】:
我使用 Visual Studio 2019 编译器。出于教育目的,我决定实现/创建一个基于模板的 FIFO 队列。为了实现这一点,我决定继承早期实现且运行良好的双向链表(也基于模板)。 下面是我为清晰呈现而特意简化的代码(我删除了所有无关紧要的成员函数声明和定义):
Doubly_linked_list.h:
#pragma once
#include <iostream>
#include <string.h>
using namespace std;
template <class T>
class list;
template <class T>
ostream& operator<<(ostream& out, const list<T>& arg);
template <class T>
class list
{
protected:
struct node
{
T* obj_ptr;
node* next_node_ptr;
node* previous_node_ptr;
node()
: next_node_ptr(NULL), previous_node_ptr(NULL), obj_ptr(NULL) {}
};
node* first_node_ptr;
node* last_node_ptr;
node* chosen_node_ptr;
int element_counter;
public:
list()
{
first_node_ptr = last_node_ptr = chosen_node_ptr = NULL;
element_counter = 0;
}
friend ostream& operator<< <T>(ostream& out, const list<T>& arg);
void set_chosen_after_last()
{
chosen_node_ptr = NULL;
}
void add_element(T& obj);
~list();
private:
void add_element_as_first(node* newly_added_node_ptr);
void add_element_as_last(node* newly_added_node_ptr);
void add_element_in_middle(node* newly_added_node_ptr);
};
//##################################################################################
//##################################################################################
template <class T>
list<T>::~list()
{
if (!first_node_ptr)
return;
node* bishop;
int i;
cout << "\n\n";
for (chosen_node_ptr = first_node_ptr, i = 0; chosen_node_ptr; i++)
{
bishop = chosen_node_ptr->next_node_ptr;
delete chosen_node_ptr;
chosen_node_ptr = bishop;
}
}
template <class T>
ostream& operator<<(ostream& out, const list<T>& arg)
{
if (!(arg.first_node_ptr))
return out;
typename list<T>::node* chaser = arg.first_node_ptr;
for (; chaser; chaser = chaser->next_node_ptr)
{
out << *(chaser->obj_ptr) << "\t";
}
return out;
}
template <class T>
void list<T>::add_element_in_middle(node* newly_added_node_ptr)
{
node* previous_node = chosen_node_ptr->previous_node_ptr;
previous_node->next_node_ptr = newly_added_node_ptr;
newly_added_node_ptr->previous_node_ptr = previous_node;
newly_added_node_ptr->next_node_ptr = chosen_node_ptr;
chosen_node_ptr->previous_node_ptr = newly_added_node_ptr;
chosen_node_ptr = newly_added_node_ptr;
}
template <class T>
void list<T>::add_element_as_last(node* newly_added_node_ptr)
{
last_node_ptr->next_node_ptr = newly_added_node_ptr;
newly_added_node_ptr->previous_node_ptr = last_node_ptr;
last_node_ptr = newly_added_node_ptr;
}
template <class T>
void list<T>::add_element_as_first(node* newly_added_node_ptr)
{
if (!first_node_ptr)
{
first_node_ptr = newly_added_node_ptr;
last_node_ptr = newly_added_node_ptr;
}
else
{
newly_added_node_ptr->next_node_ptr = first_node_ptr;
first_node_ptr->previous_node_ptr = newly_added_node_ptr;
first_node_ptr = newly_added_node_ptr;
chosen_node_ptr = first_node_ptr;
}
}
template <class T>
void list<T>::add_element(T& obj)
{
node* new_node_ptr = new node;
new_node_ptr->obj_ptr = &obj;
if (!first_node_ptr || chosen_node_ptr == first_node_ptr)
add_element_as_first(new_node_ptr);
else
{
if (!chosen_node_ptr)
{
add_element_as_last(new_node_ptr);
}
else
{
add_element_in_middle(new_node_ptr);
}
}
element_counter++;
}
队列.h:
#pragma once
#include "Doubly_linked_list.h"
template <class t>
class queue;
template <class T>
ostream& operator<<(ostream& out, typename const queue<T>& arg);
template <class T>
class queue : private list<T>
{
public:
void push(T arg)
{
list<T>::set_chosen_after_last();
list<T>::add_element(arg);
}
friend ostream& operator<< <> (ostream& out, typename const queue<T>& arg);
};
template <class T>
ostream& operator<<(ostream& out, typename const queue<T>& arg)
{
if (!(arg.list<T>::first_node_ptr))
return out;
typename list<T>::node* chaser = arg.first_node_ptr;
for (; chaser; chaser = chaser->next_node_ptr)
{
out << *(chaser->obj_ptr) << "\t";
}
return out;
}
Main.cpp:
#include <iostream>
#include "Queue.h"
int main()
{
queue<int> ex1;
ex1.push(5);
cout << ex1;
}
作为测试上述代码的结果,我希望在控制台上打印数字 5,但结果是一些随机整数(每次运行此代码时都不同),如下所示:
7599868
我开始调试我的代码并注意到 arg 是重载运算符
我现在对这种行为的可能根本原因没有任何想法。据我所知,arg 应该指代内存中与 ex1 完全相同的对象,因此它们的 obj_ptr 指针应该指向完全相同的 T 类型(在本例中为整数)对象和完全相同的值(在本例中为 5)。虽然这不是实际发生的事情。我在这里阅读了许多关于 SO 的主题,但找不到任何可以解决这种特殊情况/问题的主题。
更有趣并让我更加困惑的是,我试图通过编写一个单独的简单代码来模仿原始代码中使用的继承机制来重现这个问题。然而,这里的结果完全符合预期 - 对继承的模板类对象的引用正确地传递给重载运算符(ptr_a 指针指向的整数对象的正确值),因此控制台最终打印出正确的值:
#include <iostream>
#include <string.h>
using namespace std;
template <class T>
class Base;
template <class T>
ostream& operator<<(ostream& out, Base<T>& arg);
template <class T>
class Base
{
protected:
struct node
{
T* ptr_a;
node* next_node_ptr;
node()
{
ptr_a = new T;
next_node_ptr = NULL;
}
};
node* first_node_ptr;
public:
Base()
{
first_node_ptr = new node;
*(first_node_ptr->ptr_a) = 5;
}
friend ostream& operator<< <> (ostream& out, Base<T>& arg);
};
template <class t>
ostream& operator<<(ostream& out, Base<t>& arg)
{
cout << "this is " << *arg.first_node_ptr->ptr_a << endl;
return out;
}
////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
template <class T>
class Deriv;
template <class T>
ostream& operator<<(ostream& out, const Deriv<T>& arg);
template <class T>
class Deriv : private Base<T>
{
public:
void new_function() {}
friend ostream& operator<< <> (ostream& out, const Deriv& arg);
};
template <class T>
ostream& operator<<(ostream& out, const Deriv<T>& arg)
{
cout << "This is " << *arg.first_node_ptr->ptr_a << endl;
return out;
}
int main()
{
Base<int> ex1;
cout << ex1;
Deriv<int> ex2;
cout << ex2;
}
输出:
this is 5 This is 5
值得一提的是,重载运算符工作得很好。我还尝试使用 Code::Blocks 来测试此代码,但问题也仍然存在。正如你所看到的,我通过在实现实际的模板类定义之前首先向前声明它来与 operator 和所有以前的类似代码中对我来说效果很好。
你们中的任何人都可以指出这种情况的可能根本原因吗?我现在应该做什么才能使其按预期工作(在这种情况下打印 5 而不是一些随机数)?提前感谢您的任何想法。
【问题讨论】:
-
您的代码存储了一个指向对象的指针,该对象随后被销毁。当您取消引用此指针时,这是未定义的行为,并解释了您看到的垃圾值。
标签: c++ templates inheritance operator-overloading