队列(queue) 之 c++模板实现(友元函数和运算符重载)

一:起因

(0)拿出自己年初实现的queue队列,第一次用c++类实现queue,在和如今实现的其他复杂的STL对比,心情无比复杂;

注释:看到听到当年自己的所写所想,正的是一种享受 —— 倾听自己的幼稚也是一种美。

(1)闲话少说了,我自己现在回答自己的 三 (5) 中提到的问题,函数的返回值是用bool型还是void型??其实函数返回值是bool 还是 void是视情况而定的:例如,判空函数bool isEmpty(),比较运算符重载函数 bool operator >=(&)等判断函数; 而像交换swap(&,&),清空clear(),insert(),delete(),push,pop等一般void就可以了。

(2)再有就是是通过函数返回值返回值,还是通过参数的应用返回值呢? 仍是视情况而定的,如果返回多值,只能通过参数了;下面说一说返回单值的情况,多数是通过函数返回值返回的只有像swap(&,&) (类成员函数就一个参数的哦,默认有一个隐含的this指针),reverse()等。

(3)现在的问题是  返回单值 时是引用型(&) 还是 返回临时变量的值的形式?  第一,如果返回值的参数是该子函数的局部变量,只能以临时变量的形式返回;第二,如果在该子函数中,返回值的参数是类的成员变量(私有或共有),或者说是全局变量,可以返回引用。但是返回引用是有风险的
如,T& front(){return head->data;}
在主函数中是可以联动修改数据值的。

(4)再说const
如函数friend MyString operator+(const MyString&
t1,const MyString& t2); 说明t1 t2 是不可改变的应用参数,即其作为引用仅仅是为了减少开销,并不是作为函数返回值用的,其返回值类型是MyString类型(非引用),说明返回的是函数的局部参数。

(5)再看此函数 const
int &operator[](const size_t)const;

第一个const说的是返回值,返回的是一个常量引用,不能修改;第二个const是一个参数,表示在函数内不会修改这个参数;最后一个const是表示this指针指向的是const也就是在这个函数内,这个类所有成员都相当于是const。

第三个const
还参与对重载函数的区分,这样在参数类型个数相同的情况下形成一种新的重载形式。需要注意:常成员函数是常对象唯一的对外接口,使用使应该注意。常成员函数不能更新对象的数据成员,也不能调用该类中的普通成员函数。

(6)友元类,不多说了,请看示例吧

假设我们要设计一个模拟电视机和遥控器的程序。大家都之道,遥控机类和电视机类是不相包含的,而且,遥控器可以操作电视机,但是电视机无法操作遥控器,这就比较符合友元的特性了。即我们把遥控器类说明成电视机类的友元。下面是这个例子的具体代码:
#include <iostream>
usingnamespacestd;
classTV
{
public:
friendclassTele;
TV():on_off(off),volume(20),channel(3),mode(tv){}

(7)说明了一点,我也曾经幼稚过,现在还比较幼稚,但我相信,只要坚持最初梦想的人,终究会走向成熟;

(8)再次感谢自己能坚持最初的梦,相信你也有一个类似的梦;不要羡慕别人天赋,别人的成功是无法复制的,别人的辉煌是不会重演的;唯有我们自己脚踏实地的走好每一步,播下我们自己梦想的种子,大步向前。

二:代码展现

(1)queue队列类

 #include <iostream>
using namespace std;
template<typename T> class queue;
template<typename T>
class queue_item
{
    friend class queue<T>;
    queue_item(const T &i):item(i),next(0){}// next的默认值
    T item;
    queue_item *next;
};// 和 struct类似的哦,当然也可以改为struct的哦

template<typename T>
class queue
{
  public:
    queue():head(0),tail(0),n(0){}// ==0必须得有,head tail的初值要不要?
    queue(const queue &q);
    queue &operator= (const queue &q);
    ~queue();
    void push(const T &key);
    void pop();
    bool front(T &item);
    bool back(T &item);
    bool is_empty(){ return n == 0;  }
    size_t size()
    {
        return n;
    }
    void clear();
  private:
    queue_item<T> *head;
    queue_item<T> *tail;// queue_item没有默认构造函数,不知道为何可以这样声明;因为他是指针,只分配指针大小的内存,不分配对象内存的
    size_t n;
    void copy_item(const queue &q);
};
// 队尾加入元素
template<typename T>
void queue<T>::push(const T &key)
{
    queue_item<T> *item = new queue_item<T>(key);
    if(n == 0)
        head = tail = item;
    else
    {
        tail->next = item;// head指向队首元素
        tail = tail->next;// tail指向刚刚进入的节点
    }
    n++;
}
// 删除队首元素
template<typename T>
void queue<T>::pop()
{
    if(n == 0) return;
    else
    {
        queue_item<T> *tmp = head;
        head = head->next;
        delete tmp;
        n--;
    }
    return;
}
//返回队首元素
template<typename T>
bool queue<T>::front(T &item)
{
    if(n == 0) return false;
    else
        item = head->item;
    return true;
}
//返回队尾元素
template<typename T>
bool queue<T>::back(T &item)
{
    if(n == 0) return false;
    else
        item = tail->item;
    return true;
}

template<typename T>
void queue<T>::clear()
{
    //if(n == 0)  return false;
    while(n > 0)
    {
        pop();
    }
    //return true;
}
//复制构造函数
template<typename T>
queue<T>::queue(const queue &q):head(0),tail(0),n(0)
{
    copy_item(q);
}
// 析构函数
template<typename T>
queue<T>::~queue()
{
    clear();
}
//运算符重载函数
template<typename T>
queue<T> &queue<T>::operator = (const queue &q)
{
    if(this != &q)
    {
        clear();
        n = 0;
        copy_item(q);
    }
    return *this;
}
// 深度复制函数
template<typename T>
void queue<T>::copy_item(const queue &q)
{
    queue_item<T> *tmp = q.head;
    while(tmp != NULL)
    {
        push(tmp->item);
        tmp = tmp->next;
    }
}

(2)main函数测试

#include <cstdlib>
#include <iostream>
#include <string>

using namespace std;

// test the queue class template
int main(int argc, char *argv[]) {
	queue<int> q;
	int front,back;
	if (q.is_empty())
		cout << "empty" << endl;
	else
		cout << "not empty" << endl;
	q.push(1);
	q.push(2);
	q.push(3);
	q.push(4);
	queue<int> q2(q);
	q.clear();
	if (q.is_empty())
		cout << "empty" << endl;
	else
		cout << "not empty" << endl;
    q2.front(front);
    q2.back(back);
	cout << "queue 2: " << front << endl;
	cout << "queue 2: " << back << endl;
	cout << "the size of queue 2: " << q2.size() << endl;
	q = q2;
	q.front(front);
    q.back(back);
	cout << "queue 1: " << front << endl;
	cout << "queue 1: " << back << endl;
	queue<string> qs;
	qs.push("gauss");
	qs.push("randy");
	qs.push("jiawenjie");
	string s1,s2;
	qs.front(s1);
	qs.back(s2);
	cout << s1 << endl;
	cout << s2 << endl;
	return EXIT_SUCCESS;
}

三:体会和总结

1、如果一个类中定义了任何一种构造函数,而未定义默认构造函数,是不能够定义类的对象的如:class A{}; A  aa;(这是错误的)

但是A *pa;(这是合法的,因为仅仅定义了一个指针,没有调用任何构造函数,不分配类对象的内存)

2、类的private和protected成员是不能被类外访问,但是,此规则不影响friend(友元),如果我们想声明一个外部函数作为类的

friend(友元),从而使此函数访问这类的private和protected成员,我们通过这个外部函数的原型声明在类体里,前面加上关键字friend.

3、将A类声明为B类的友元(朋友),此时A类就是B类的友元类,友元类A的所有函数都是B类的友元函数,可以访问B类的所有私有成员

变量,但不等于B类是A类的友元类,B类的成员函数不能访问A类的私有成员变量.

4 、运算符重载一般也声明为友元函数(friend),

5、现在还有两点不是很明白,请大牛指点——函数的返回值是用bool型还是void型??再有就是是通过函数返回值返回值,还是通过参数的应用返回值呢?

时间: 2024-11-18 21:45:06

队列(queue) 之 c++模板实现(友元函数和运算符重载)的相关文章

go语言笔记——是c开发的 lex yacc进行词法和语法分析,go不支持函数和运算符重载,不支持类型继承,也不支持断言,还有泛型

从 Go 1.0.3 版本开始,不再使用 8g,8l 之类的指令进行程序的构建,取而代之的是统一的 go build 和 go install 等命令,而这些指令会自动调用相关的编译器或链接器. 如果你想获得更深层次的信息,你可以在目录 $GOROOT/src/cmd 下找到编译器和链接器的源代码.Go 语言本身是由 C 语言开发的,而不是 Go 语言(Go 1.5 开始自举).词法分析程序是 GNU bison,语法分析程序是名为 $GOROOT/src/cmd/gc/go.y 的 yacc

类模板,多种类型的类模板,自定义类模板,类模板的默认类型,数组的模板实现,友元和类模板,友元函数,类模板与静态变量,类模板与普通类之间互相继承,类模板作为模板参数,类嵌套,类模板嵌套,类包装器

 1.第一个最简单的类模板案例 #include "mainwindow.h" #include <QApplication> #include <QPushButton> #include <QLabel> template<class T> class run { public: T w; void show() { w.show(); } void settext() { w.setText("A"); }

友元函数实现操作符重载的应用场景-友元函数实现左移右移操作符重载

先定义一个测试类Complex,其中用成员函数的方法重载了+.-.前置++.前置--.后置++.后置--这6个运算符,当然,这6个操作符也可以用友元函数方式重载,但习惯上这些都直接用成员函数方式重载. demo #include <iostream> using namespace std; class Complex { public: Complex(int a = 0, int b = 0) { this->a = a; this->b = b; } ~Complex();

关于类模板的友元函数

转载 http://blog.sina.com.cn/s/blog_69dd1a090101fc59.html 问题始于学习数据结构,自己编写一个单链表,其中用到了重载输出运算符<<,我写的大约这样: 1 template <class T> class List{ 2 friend std::ostream& operator << (std::ostream& os,const List<T>& slist); 3 //…… 4

4 C++基础4 类 const函数 转全局函数 返回*this 数组类。友元 函数 类 操作符重载

1,请问类中函数 const修饰的谁? [email protected]:~/c++$ cat main.cpp  #include <iostream> #include <stdlib.h> using namespace std; class A { public: //const的三种写法 //const void fun(int a,int b) //void const fun(int a,int b) //void fun(int a,int b) const vo

C++ 模板类友元之输出流操作符重载

几个关键点: 需要前置声明!--奇怪的是别人告诉我也可以不需要,但我这里不行! 友元函数的函数名后面的<>,必须要有. #include <stdio.h> #include <iostream> using namespace std; //前置声明,你妹啊 template<class T> class A; template<class T> ostream &operator<< (ostream &out,

关于友元函数,运算符重载和类的类型转换

一 关于运算符的重载:运算符中有10种是不可重载的:sizeof static_cast<type>. dynamic_cast<type>.* const_cast<type>:: typeid?= reinterpret_cast<type>有4种是只可以在类中重载的:() [] = -> (1)同时在重载时需要有一个参数是用户自己定义的类成员,否则无法使用 (2)运算符的使用方式,及所需参数个数与重载前所需的参数个数相同 重载的格式: retur

友元函数&lt;&lt;的模板化

1.构造函数的一种使用 int main(void){     //Test t1 = 10;// 在构造函数中寻找只有一个参数的          //手动的调用构造函数;     Test t2 = Test(1, 2);  //直接调用构造函数--->匿名对象;     //直接调用Test的有参构造函数,初始化t2对象;  只会调用一次构造函数(直接把匿名对象转成t2);      } 2.构造与赋值语句 Test g(){          //返回为对象,我就给你返回一个新的对象(匿

模板类的友元函数

非模板友元函数 模板类的非模板友元函数是说该模板类的友元函数只是一个普通函数,并且该函数是非模板函数或该函数不视为模板函数.这里包含了两种情况,下面分别就两个例子进行说明. • 函数是非模板函数 这一类友元函数特点是不带有参数列表,例如:friend void Fn().这类友元函数通常可以用于全局对象的访问. #include <iostream> using namespace std; template <class T> class MyNumber { private: