Daya Jin's Blog Python and Machine Leaning

C++ Advance

2019-09-08


友元与运算符重载

运算符重载等同于函数重载,原生运算符仅支持少数几个数据类型,重载运算符可支持自定义类型。现定义一个二维向量类:

class Vec
{
	double x;
	double y;

public:
	Vec(double x, double y);
}

Vec::Vec(double x = 0, double y = 0) {
	this->x = x;
	this->y = y;
}

要求为该类实现乘法,有三种:Vec*VecVec*xx*Vec。前两种乘法可直接使用成员函数来实现,如下所示:

Vec Vec::operator*(const Vec& v) {
	Vec res;
	res.x = this->x * v.x;
	res.y = this->y * v.y;
	return res;
}

Vec Vec::operator*(double r) {
	Vec res;
	res.x = this->x * r;
	res.y = this->y * r;
	return res;
}

问题是第三种。第三种乘法x*Vec因为向量类在右边,所以不能使用成员函数来实现,但是使用非成员函数的话会存在访问权限问题。C++中友元关键字friend修饰的函数和对象可访问类的私有属性。因此Vec.h内容为:

#include<iostream>
using namespace std;

class Vec
{
	double x;
	double y;

public:
	Vec(double x, double y);
	Vec operator*(const Vec& v);    // Vec*Vec
	Vec operator*(double r);    // Vec*r
	friend Vec operator*(double r, const Vec& v);    // r*Vec, 友元函数可访问私有属性
	friend ostream& operator<<(ostream& cout, const Vec& v);    // 重载输出
};

Vec.cpp内容为:

#include<iostream>
#include "Vec.h"
using namespace std;

Vec::Vec(double x = 0, double y = 0) {
	this->x = x;
	this->y = y;
}

Vec Vec::operator*(const Vec& v) {
	Vec res;
	res.x = this->x * v.x;
	res.y = this->y * v.y;
	return res;
}

Vec Vec::operator*(double r) {
	Vec res;
	res.x = this->x * r;
	res.y = this->y * r;
	return res;
}

Vec operator*(double r, const Vec& v) {
	Vec res;
	res.x = r * v.x;
	res.y = r * v.y;
	return res;
}

ostream& operator<<(ostream& cout, const Vec& v) {
	cout << "<" << v.x << ", " << v.y << ">";
	return cout;    // 为了支持连续输出
}

模版

函数模版

当函数需要能够支持多种数据类型时,重载是一种策略,重载的问题在于需要为每一种数据类型都写一套函数代码。如果对于不同的数据类型,函数的逻辑是一样,函数重载就等同于写重复代码,对于这种情况,C++提供了模版机制,可将不同数据类型下的函数逻辑抽象成模版,而具体的类型待指定。

函数模版与函数重载的区别:

  • 函数模版对不同数据类型的处理逻辑是一模一样的,而重载函数的逻辑可以不一样;
  • 在参数数量不同或者逻辑不同的情况下只能使用函数重载,否则可以使用函数模版;
  • 模版也是可重载的,主要解决参数数量不同和逻辑不同的问题。

比如说交换函数Swap(a,b),不管参数属于整形、浮点、字符、还是其他自定义的数据类型,交换两个物体的函数逻辑是一摸一样的,因此可以将该函数抽象成模版。

#include<iostream>
using namespace std;

template<typename T>
void Swap(T* a, T* b) {
	T tmp = *a;
	*a = *b;
	*b = tmp;
}

int main(void) {
	int a = 1;
	int b = 2;
	Swap<int>(&a, &b);
	cout << a << ' ' << b << endl;

	float c = 1.0f;
	float d = 2.0f;
	Swap<float>(&c, &d);
	cout << c << ' ' << d << endl;

	return 0;
}

类模板

模版这里有一个坑:模版函数的声明与实现必须都在头文件中,这是微软给出的建议,为什么要这么做的原因见此。下面实现一个弱智的栈类,Stk.h

#include<iostream>
using namespace std;

template<typename T>
class Stk {
	T* base;
	int cap;
	int size;

public:
	Stk(int cap);
	Stk(const Stk& arr);    // 拷贝构造
	~Stk();
	Stk& operator= (const Stk& arr);    // 赋值重载

	void append(const T& x);
	T top();
};

template<typename T>
Stk<typename T>::Stk(int cap) {
	this->cap = cap;
	this->size = 0;
	this->base = new T[this->cap];
}

template<typename T>
Stk<typename T>::Stk(const Stk& arr) {
	this->cap = arr.cap;
	this->size = arr.size;
	this->base = new T[this->cap];

	for (int i = 0; i < this->size; i++) {
		this->base[i] = arr.base[i];
	}
}

template<typename T>
Stk<typename T>::~Stk() {
	if (this->base != NULL) {
		delete[] this->base;
		this->base = NULL;
		this->size = 0;
	}
}

template<typename T>
Stk<T>& Stk<typename T>::operator=(const Stk& arr) {
	this->~Arr();
	this->cap = arr.cap;
	this->size = arr.size;
	this->base = new T[this->cap];

	for (int i = 0; i < this->size; i++) {
		this->base[i] = arr.base[i];
	}
}

template<typename T>
void Stk<typename T>::append(const T& x) {
	if (this->size == this->cap) {
		return;
	}
	this->base[this->size] = x;
	this->size += 1;
}

template<typename T>
T Stk<typename T>::top() {
	return this->base[size-1];    // 不考虑异常
}

 main.h

#include<iostream>
#include<typeinfo>
#include "Stk.h"
using namespace std;

int main()
{
	Stk<int> s1 = Stk<int>(5);
	s1.append(0);
	Stk<int> s2 = Stk<int>(s1);
	s2.append(1);
	Stk<int> s3 = s2;
	cout<<s3.top()<<endl;

	Stk<float> s4 = Stk<float>(5);
	s4.append(1.2f);
	cout << s4.top() << endl;

	return 0;
}

Similar Posts

上一篇 C++ Basic

下一篇 C++ STL

Content