C++ 中的模板 类 和函数重载
Template Classes in C++ and Function Overloading
我正在练习在 C++ 中使用模板和 classes。我的目标是为双端队列编写一个模板 class。它将具有 "insert_head"、"insert_tail"、"remove_tail" 和 "remove head" 的功能,以及使用 "cout" 打印的功能。此外,'=' 运算符必须能够用于将 class 的一个实例复制到另一个实例。这是我当前的代码:
#ifndef DEQUE_H
#define DEQUE_H
template <typename T>
class Deque {
public:
Deque(int size = 0, int capacity = 1000) : size_(size), capacity_(capacity)
{}
Deque(Deque & d) : x_(d.x()), size_(d.size()), capacity_(d.capacity()) {}
std::ostream & operator<<(std::ostream & cout) {
cout << '[';
if (size_ > 0) {
for (int i = 0; i < (size_ - 1)* sizeof(T); i += sizeof(T)) {
std::cout << *(x_ + i) << ',';
}
cout << *(x_ + (size_ - 1)* sizeof(T));
}
cout << ']';
return cout;
}
Deque operator=(Deque d) {
Deque dq(d);
return dq;
}
void print_test() {
std::cout << '[';
if (size_ > 0) {
for (int i = 0; i < (size_ - 1)* sizeof(T); i += sizeof(T)) {
std::cout << *(x_ + i) << ',';
}
std::cout << *(x_ + (size_ - 1)* sizeof(T));
}
std::cout << ']';
}
int * x() {
return x_;
}
int size() {
return size_;
}
int capacity() {
return capacity_;
}
bool is_empty() {
return size_ == 0;
}
void insert_tail(T tail) {
if (size_ < capacity_) {
*(x_ + sizeof(T) * size_) = tail;
size_++;
} else {
// throw overflow
}
}
T remove_tail() {
if (size_ > 0) {
T ret = *(x_ + sizeof(T) * (size_ - 1));
std::cout << ret;
size_--;
return ret;
} else {
// throw underflow
}
}
void insert_head(T head) {
if (size_ > 0 && size_ < capacity_) {
for (int i = (size_ - 1) * sizeof(T); i < 0; i -= sizeof(T)) {
*(x_ + i + sizeof(T)) = *(x_ + i);
}
}
if (size_ < capacity_) {
*x_ = head;
size_++;
} else {
// throw overflow
}
}
T remove_head() {
if (size_ > 0) {
T ret = *x_;
for (int i = sizeof(T); i < size_* sizeof(T); i += sizeof(T)) {
*(x_ + i - sizeof(T)) = *(x_ + i);
}
size_--;
return ret;
} else {
// throw underflow
}
}
private:
T * x_;
int size_;
int capacity_;
};
#endif
这是我使用 class:
的测试代码
#include <iostream>
#include "Deque.h"
int main(int argc, char const *argv[])
{
Deque< int > dq;
dq.insert_head(1);
// dq.insert_head(2); // adding head when not empty causes bug
dq.insert_tail(3);
dq.insert_tail(4);
dq.insert_tail(5);
dq.print_test(); std::cout << std::endl;
// std::cout << dq; // '<<' not overloaded properly'
std::cout << dq.remove_head() << " head removed\n";
// int x = dq.remove_head(); // seg faults when assigning returned value to a variable
dq.insert_tail(2);
dq.print_test();
std::cout << std::endl;
Deque< int > dq1(dq);
Deque< int > dq2;
// dq2 = dq1; // '=' not overloaded properly
return 0;
}
我的四个问题中的每一个都在我的测试文件中注释掉的代码行中,这里有进一步的解释:
当调用 "dq.insert_head(2)" 并且 dq 不为空(大小 > 0)时,我尝试将双端队列中的所有其他元素移动到一个位置,以便我可以在那里插入新值,有问题,元素没移过来
"std::cout << dq" 没有按应有的方式打印 dq。该代码与 "print_test()" 方法非常相似,但是当我 运行 程序时,我得到错误 "no match for operator <<"。这是因为它是模板 class 吗?还是我做的其他事情完全错了?
当试图从双端队列中删除头或尾时,我正在尝试 return 删除的值。在未注释掉的代码行中,returned 值按原样打印,但以下代码行导致段错误。是不是因为我试图将模板 varabale 分配给整数变量?
我的最后一个问题是“=”运算符没有将 class 的一个实例复制到另一个实例。我的目标是创建 class 的新实例,然后创建 return 该实例(正如您在 "Deque operator=(Deque d)" 中看到的那样),但这并没有像我希望的那样工作。使用模板 classes.
重载 '=' 函数的最佳方法是什么
感谢您的帮助,非常感谢您对这些问题的回答。
你第一个问题的答案是删除 sizeof(T)
所以你最终得到这个
for (int i = (size_ - 1); i > 0; i --) {
*(x_ + i + 1) = *(x_ + i);
}
第二个问题的答案是将 <<
重载的声明更改为 friend std::ostream & operator<<(std::ostream & x, Deque n)
并在 class.
之外初始化正文
第三个问题的答案是您不能 return 一个可能指向与 T 不同的内存位置块的 int 指针。
第四个问题的答案如下:
Deque& operator=(const Deque& d) {
x_ = d.x_; // Deep copy
size_ = d.size_;
capacity_ = d.capacity_;
return *this;
}
你所有的函数都有问题:
Deque(int size = 0, int capacity = 1000) : size_(size), capacity_(capacity) {}
- 如果允许指定大小,则必须为这些项目分配和初始化内存。您应该只指定容量。
- x_ 未初始化。
假设,你想要一个固定的容量,那么你的构造函数应该是:
Deque(int capacity = 1000)
: size_(0)
, x_(new T[capacity])
, capacity_(capacity)
{
}
即使这是一个简化版本,因为它会为所有可能效率低下的项目调用构造函数,并且需要 T
具有可访问的默认构造函数。
现在复制构造函数:
- 拷贝构造函数应该做深拷贝。否则,您的程序将(可能)在删除您已为其复制的第一个
Deque
后崩溃,因为删除一个项目两次是未定义的行为。
- 原型应采用常量引用,如:
Deque(const Deque &other);
代码看起来类似于:
Deque(const Deque &other)
: capacity_(other.capacity_)
, x_(new T[other.capacity_])
, size_(other.size_)
{
for (int i = 0; i != size_; ++i)
{
x_[i] = other.x_[i];
}
}
对于<<
,原型应该是:
friend std::ostream & operator<<(std::ostream &cout, const T &data)
假设它在 class 内声明以访问私有字段。您需要传递操作员工作的数据。
对于赋值运算符,类似这样的东西可以工作:
Deque& operator=(const Deque &other)
{
// Use swap idiom...
Deque tmp(other);
// Swap pointers so old x_ get destroyed...
T *old_x = x_;
x_ = tmp.x_;
tmp.x_ = old_x;
// Usually one would use std::swap.
// Here as tmp get destroyed, it is not strictly to swap capacity_ and size_.
capacity_ = tmp.capacity_;
size_ = tmp.size_;
}
现在 x()
函数:
- 如果你做一个队列,你可能不想公开数据所以这个功能应该被删除。
- 如果它被保留,函数应该是 const 和 returns 指向 T 的指针:T *x() const;
以获得预期的功能。
size
、capacity
和 is_empty
应该都是 const
成员函数。
insert_tail
和 remove_tail
问题已在其他人的评论中进行了解释(特别是无关的 sizeof
)。
insert_head
和 remove_head
也有类似的问题。此外,复制现有项目的代码可以在私有函数中重构,以遵循 DRY 原则并避免代码重复。
我正在练习在 C++ 中使用模板和 classes。我的目标是为双端队列编写一个模板 class。它将具有 "insert_head"、"insert_tail"、"remove_tail" 和 "remove head" 的功能,以及使用 "cout" 打印的功能。此外,'=' 运算符必须能够用于将 class 的一个实例复制到另一个实例。这是我当前的代码:
#ifndef DEQUE_H
#define DEQUE_H
template <typename T>
class Deque {
public:
Deque(int size = 0, int capacity = 1000) : size_(size), capacity_(capacity)
{}
Deque(Deque & d) : x_(d.x()), size_(d.size()), capacity_(d.capacity()) {}
std::ostream & operator<<(std::ostream & cout) {
cout << '[';
if (size_ > 0) {
for (int i = 0; i < (size_ - 1)* sizeof(T); i += sizeof(T)) {
std::cout << *(x_ + i) << ',';
}
cout << *(x_ + (size_ - 1)* sizeof(T));
}
cout << ']';
return cout;
}
Deque operator=(Deque d) {
Deque dq(d);
return dq;
}
void print_test() {
std::cout << '[';
if (size_ > 0) {
for (int i = 0; i < (size_ - 1)* sizeof(T); i += sizeof(T)) {
std::cout << *(x_ + i) << ',';
}
std::cout << *(x_ + (size_ - 1)* sizeof(T));
}
std::cout << ']';
}
int * x() {
return x_;
}
int size() {
return size_;
}
int capacity() {
return capacity_;
}
bool is_empty() {
return size_ == 0;
}
void insert_tail(T tail) {
if (size_ < capacity_) {
*(x_ + sizeof(T) * size_) = tail;
size_++;
} else {
// throw overflow
}
}
T remove_tail() {
if (size_ > 0) {
T ret = *(x_ + sizeof(T) * (size_ - 1));
std::cout << ret;
size_--;
return ret;
} else {
// throw underflow
}
}
void insert_head(T head) {
if (size_ > 0 && size_ < capacity_) {
for (int i = (size_ - 1) * sizeof(T); i < 0; i -= sizeof(T)) {
*(x_ + i + sizeof(T)) = *(x_ + i);
}
}
if (size_ < capacity_) {
*x_ = head;
size_++;
} else {
// throw overflow
}
}
T remove_head() {
if (size_ > 0) {
T ret = *x_;
for (int i = sizeof(T); i < size_* sizeof(T); i += sizeof(T)) {
*(x_ + i - sizeof(T)) = *(x_ + i);
}
size_--;
return ret;
} else {
// throw underflow
}
}
private:
T * x_;
int size_;
int capacity_;
};
#endif
这是我使用 class:
的测试代码#include <iostream>
#include "Deque.h"
int main(int argc, char const *argv[])
{
Deque< int > dq;
dq.insert_head(1);
// dq.insert_head(2); // adding head when not empty causes bug
dq.insert_tail(3);
dq.insert_tail(4);
dq.insert_tail(5);
dq.print_test(); std::cout << std::endl;
// std::cout << dq; // '<<' not overloaded properly'
std::cout << dq.remove_head() << " head removed\n";
// int x = dq.remove_head(); // seg faults when assigning returned value to a variable
dq.insert_tail(2);
dq.print_test();
std::cout << std::endl;
Deque< int > dq1(dq);
Deque< int > dq2;
// dq2 = dq1; // '=' not overloaded properly
return 0;
}
我的四个问题中的每一个都在我的测试文件中注释掉的代码行中,这里有进一步的解释:
当调用 "dq.insert_head(2)" 并且 dq 不为空(大小 > 0)时,我尝试将双端队列中的所有其他元素移动到一个位置,以便我可以在那里插入新值,有问题,元素没移过来
"std::cout << dq" 没有按应有的方式打印 dq。该代码与 "print_test()" 方法非常相似,但是当我 运行 程序时,我得到错误 "no match for operator <<"。这是因为它是模板 class 吗?还是我做的其他事情完全错了?
当试图从双端队列中删除头或尾时,我正在尝试 return 删除的值。在未注释掉的代码行中,returned 值按原样打印,但以下代码行导致段错误。是不是因为我试图将模板 varabale 分配给整数变量?
我的最后一个问题是“=”运算符没有将 class 的一个实例复制到另一个实例。我的目标是创建 class 的新实例,然后创建 return 该实例(正如您在 "Deque operator=(Deque d)" 中看到的那样),但这并没有像我希望的那样工作。使用模板 classes.
重载 '=' 函数的最佳方法是什么
感谢您的帮助,非常感谢您对这些问题的回答。
你第一个问题的答案是删除 sizeof(T)
所以你最终得到这个
for (int i = (size_ - 1); i > 0; i --) {
*(x_ + i + 1) = *(x_ + i);
}
第二个问题的答案是将 <<
重载的声明更改为 friend std::ostream & operator<<(std::ostream & x, Deque n)
并在 class.
第三个问题的答案是您不能 return 一个可能指向与 T 不同的内存位置块的 int 指针。
第四个问题的答案如下:
Deque& operator=(const Deque& d) {
x_ = d.x_; // Deep copy
size_ = d.size_;
capacity_ = d.capacity_;
return *this;
}
你所有的函数都有问题:
Deque(int size = 0, int capacity = 1000) : size_(size), capacity_(capacity) {}
- 如果允许指定大小,则必须为这些项目分配和初始化内存。您应该只指定容量。
- x_ 未初始化。
假设,你想要一个固定的容量,那么你的构造函数应该是:
Deque(int capacity = 1000)
: size_(0)
, x_(new T[capacity])
, capacity_(capacity)
{
}
即使这是一个简化版本,因为它会为所有可能效率低下的项目调用构造函数,并且需要 T
具有可访问的默认构造函数。
现在复制构造函数:
- 拷贝构造函数应该做深拷贝。否则,您的程序将(可能)在删除您已为其复制的第一个
Deque
后崩溃,因为删除一个项目两次是未定义的行为。 - 原型应采用常量引用,如:
Deque(const Deque &other);
代码看起来类似于:
Deque(const Deque &other)
: capacity_(other.capacity_)
, x_(new T[other.capacity_])
, size_(other.size_)
{
for (int i = 0; i != size_; ++i)
{
x_[i] = other.x_[i];
}
}
对于<<
,原型应该是:
friend std::ostream & operator<<(std::ostream &cout, const T &data)
假设它在 class 内声明以访问私有字段。您需要传递操作员工作的数据。
对于赋值运算符,类似这样的东西可以工作:
Deque& operator=(const Deque &other)
{
// Use swap idiom...
Deque tmp(other);
// Swap pointers so old x_ get destroyed...
T *old_x = x_;
x_ = tmp.x_;
tmp.x_ = old_x;
// Usually one would use std::swap.
// Here as tmp get destroyed, it is not strictly to swap capacity_ and size_.
capacity_ = tmp.capacity_;
size_ = tmp.size_;
}
现在 x()
函数:
- 如果你做一个队列,你可能不想公开数据所以这个功能应该被删除。
- 如果它被保留,函数应该是 const 和 returns 指向 T 的指针:T *x() const;
以获得预期的功能。
size
、capacity
和 is_empty
应该都是 const
成员函数。
insert_tail
和 remove_tail
问题已在其他人的评论中进行了解释(特别是无关的 sizeof
)。
insert_head
和 remove_head
也有类似的问题。此外,复制现有项目的代码可以在私有函数中重构,以遵循 DRY 原则并避免代码重复。