C++基础(五)模版

1. 友元函数

函数有全局函数、成员函数,如果把全局函数定义为友元,就叫友元全局函数,如果把成员函数定义为友元,就叫友元成员函数

1.1 友元全局函数

class Coordinate
{
	friend void printXY(Coordinate& c);
public:
	Coordinate(int x,int y);
private:
	int m_iX;
	int m_iY;
};
void printXY(Coordinate& c)
{
	cout<<c.m_iX<<" "<<c.m_iY<<endl;//直接访问私有成员
}
//用法
Coordinate coor(3,5);
printXY(coor);

友元函数参数需要传这个类的对象或者它的引用或者它的指针,可以直接访问对象的私有或者保护成员。

1.2 友元成员函数

class Coordinate
{
	friend void Circle::printXY(Coordinate& c);//将Circle的printXY函数声明为Coordinate友元函数
public:
	Coordinate(int x,int y);
private:
	int m_iX;
	int m_iY;
};
class Circle
{
public:
	void printXY(Coordinate& c)
	{
		cout<<c.m_iX<<" "<<c.m_iY<<endl;//直接访问私有成员
	}
};
//用法
Coordinate coor(3,5);
Circle circle;
circle.printXY(coor);

将Circle的printXY函数声明为Coordinate友元函数,Circle就可以直接访问Coordinate的私有或保护成员。但这样就破坏了Coordinate的封装性,除非有特殊需要,否则不建议经常使用友元。

2. 友元类

定义友元类之前要先声明一下这个类。

class Circle;
class Coordinate
{
	friend Circle;//定义友元类
public:
	Coordinate(int x,int y);
private:
	int m_iX;
	int m_iY;
};

这样就可以在Circle类中定义Coordinate对象,并且通过这个对象任意访问Coordinate的私有或保护成员。

class Circle
{
public:
	void printXY()
	{
		cout<<m_coor.m_iX<<" "<<m_coor.m_iY<<endl;
	}
private:
	Coordinate m_coor;
};

这样任何Circle的成员都可以访问Coordinate的成员。

友元的注意事项:

  1. 友元关系不可传递
    C是B的友元,B是A的友元,但C不一定是A的友元,反之亦然。
  2. 友元关系是单向的
    B是A的友元,A不一定是B的友元
  3. 友元声明的形式及数量不受限制
  4. 友元只是封装的补充,破坏了封装性,应尽量避免使用

3. 静态

class Tank
{
public:
	Tank(){}
	~Tank(){}
	static int getCount(){return count;}//静态成员函数
	static int count;//静态数据成员
private:
	string m_code;
};
int Tank::count=0;
//使用
cout<<Tank::getCount()<<endl;
cout<<Tank::count()<<endl;
Tank tank;
cout<<tank.getCount()<<endl;
cout<<tank.count()<<endl;

静态数据成员并不依赖于对象,而是依赖于类,也就是说,就算不实例化对象,静态数据成员count仍然存在于内存中,如果是普通的数据成员,必须要实例化才存在于内存中。
由于静态数据成员不依赖于对象的实例化,因此并不会在构造函数中初始化,需要单独初始化,初始化时不要再加static关键字。
静态成员可以通过类和对象访问。
对象的数据成员存在于内存之前静态成员就已经存在了。普通成员函数可以访问静态成员,因为静态成员不与对象相关;静态成员函数不能访问非静态的数据成员,因为静态成员是依赖于类的,普通成员是依赖于对象的。像下面这样事错误的:

static int getCount()
{
	m_code="01";//错误
	return count;
}

从this指针的角度看,普通的成员函数其实有一个参数this指针,而静态成员函数并不会传入隐形的this指针,因此并不知道访问的是哪个对象的数据成员,所以不能访问非静态成员。但静态成员函数可以访问静态成员。
静态使用注意事项:

  1. 静态数据成员必须单独初始化,不能写到构造函数中
  2. 静态成员函数不能调用非静态成员函数和非静态数据成员
  3. 静态数据成员只有一份,不依赖于对象而存在。如果用sizeof求对象大小,是不包括静态数据成员的

4. 运算符重载

运算符重载的本质就是函数重载。关键字operator,包括一元运算符重载(-负号,++自增),只与一个操作数进行运算,二元运算符重载
一元运算符重载:
有两种方式,友元函数重载和成员函数重载。
友元函数重载中传的参数分别是操作符左边的数,操作符右边的数,成员函数重载传入的参数是操作符右边的数,当前对象调用

4.1 一元运算符重载

4.1.1 重载-

  1. 成员函数重载
class Coordinate
{
public:
	Coordinate(int x,int y);
	Coordinate& operator-();//-号重载,作为一元运算符并且是成员函数,不需要传任何参数
private:
	int m_iX;
	int m_iY;
}
Coordinate& Coordinate::operator-()
{
	m_iX=-m_iX;
	m_iY=-m_iY;
	return *this;
}
//使用
Coordinate coor(3,5);
-coor;//相当于coor.operator-()
  1. 友元函数重载
class Coordinate
{
friend Coordinate& operator-(Coordinate& coor);//-号重载,需要传参数,通过引用变量,取反赋给它本身
public:
	Coordinate(int x,int y);
private:
	int m_iX;
	int m_iY;
}
Coordinate& operator-(Coordinate& coor)
{
	coor.m_iX=-coor.m_iX;
	coor.m_iY=-coor.m_iY;
	return *this;
}
//使用
Coordinate coor(3,5);
-coor;//相当于operator-(coor)

4.1.2 重载++

  1. 前置++
class Coordinate
{
public:
	Coordinate(int x,int y);
	Coordinate& operator++();//前置++
private:
	int m_iX;
	int m_iY;
}
Coordinate& Coordinate::operator++()
{
	m_iX++;
	m_iY++;//这样接收的值就是++之后的值
	return *this;
}
//使用
Coordinate coor(3,5);
++coor;//相当于coor.operator++()
  1. 后置++
class Coordinate
{
public:
	Coordinate(int x,int y);
	Coordinate operator++(int);//后置++
private:
	int m_iX;
	int m_iY;
}
Coordinate Coordinate::operator++(int)
{
	Coordinate old(*this);
	m_iX++;
	m_iY++;//这样接收的值就是++之后的值
	return old;//返回旧值
}
//使用
Coordinate coor(3,5);
coor++;//相当于coor.operator++(0);系统默认传入一个值0,但没有任何意义,只是标识

后置++返回值不再是引用,而是Coordinate对象,参数要传入int,用来标识当前的++是后置重载,使用时不需要传入任何值,只是一个标识而已。

4.2 二元运算符重载

4.2.1 重载+

  1. 成员函数重载
class Coordinate
{
public:
	Coordinate(int x,int y);
	Coordinate operator+(const Coordinate& coor);
private:
	int m_iX;
	int m_iY;
}
Coordinate Coordinate::operator+(const Coordinate& coor)//隐含this指针,指向“+”左边的对象
{
	Coordinate tmp;
	tmp.m_iX=this->m_iX+coor.m_iX;
	tmp.m_iY=this->m_iY+coor.m_iY;
	return tmp;
}
//使用
Coordinate coor1(3,5);
Coordinate coor2(4,7);
Coordinate coor3(0,0);
coor3=coor1+coor2;//相当于coor1.operator+(coor2)
  1. 友元函数重载
class Coordinate
{
friend Coordinate operator+(const Coordinate& c1,const Coordinate& c2);//const也可以不写,写了表示无法修改加数的值,是一种规范
public:
	Coordinate(int x,int y);
private:
	int m_iX;
	int m_iY;
}
Coordinate operator+(const Coordinate& c1,const Coordinate& c2)
{
	Coordinate tmp;
	tmp.m_iX=c1.m_iX+c2.m_iX;
	tmp.m_iY=c1.m_iY+c2.m_iY;
	return tmp;
}
//使用
Coordinate coor1(3,5);
Coordinate coor2(4,7);
Coordinate coor3(0,0);
coor3=coor1+coor2;//相当于operator+(coor1,coor2)

4.2.2 重载<<

class Coordinate
{
friend ostream& operator<<(ostream& out,const Coordinate& coor);//返回值必须是ostream&,第一个参数必须是ostream& out
public:
	Coordinate(int x,int y);
private:
	int m_iX;
	int m_iY;
}
ostream& operator<<(ostream& out,const Coordinate& coor)
{
	out<<coor.m_iX<<","<<coor.m_iY;
	return out;
}
//使用
Coordinate coor(3,5);
cout<<coor;//相当于operator<<(cout,coor)

<<操作符不能用成员函数重载,因为函数第一个参数必须是ostream,不能是this指针或当前对象

4.2.3 重载索引运算符[]

class Coordinate
{
public:
	Coordinate(int x,int y);
	int operator[](int index);
private:
	int m_iX;
	int m_iY;
}
int Coordinate::operator[](int index)
{
	if(0==index)
		return m_iX;
	if(1==index)
		return m_iY;
	//否则抛出异常
}
//使用
Coordinate coor(3,5);
cout<<coor[0];//相当于coor.operator[](0)
cout<<coor[1];//相当于coor.operator[](1)

[]操作符不能用友元函数重载,因为友元函数重载,第一个参数可以是成员函数重载中的那个this指针,也可以是其他值,而[]函数重载第一个参数必须是this指针,这样才能够传入索引并且表达当前对象的数据成员

5. 函数模版

关键字template typename class

template<class T>
T max(T a,T b)//函数模版
{
	return (a>b)?a:b;
}
//使用
int val=max(100,99);//不指定数据类型T,计算机会根据自己的判断选择一种模版函数
char cval=max<char>('A','B');
//使用函数模版生产出的函数叫模版函数

如果只写了函数模版而没有使用,计算机是不会产生任何代码数据的,只有使用函数模版时计算机才知道具体实例化怎样的模版函数。

变量作为模版参数

template<int size>
void display()
{
	cout<<size<<endl;
}
//使用
display<10>();

多参数函数模版

template<typename T,typename C>
void display(T a,C b)
{
	cout<<a<<" "<<b<<endl;
}
//使用
int a=1024;string s="hello";
display<int,string>(a,s);

typename和class可以混用,如

template<typename T,class C>
T minus(T *a,C b);

也可以这样混用

template<typename T,int size>
void display(T a)
{
	for(int i=0;i<size;i++)
		cout<<a<<endl;
}
//使用
display<int,5>(15);

函数模版与重载

//互为重载
template<typename T>
void display(T a);
template<typename T>
void display(T a,T b);
template<typename T,int size>
void display(T a);
//使用
display<int>(10);
display<int>(10,10);
display<int,5>(10);

6. 类模版

template<class T>
class MyArray
{
public:
	void display()//类内定义的成员函数
	{...}
private:
	T *m_pArr;
};
//使用
MyArray<int> arr;
arr.display();

如果类模版的成员函数在类外定义,每个函数形式应如下:

template<class T>
void MyArray<T>::display()
{...}

与函数模版一样,类模版本身不会产生实质性的代码,只有在实例化对象时才会产生一套代码,叫做模版类。

多参数类模版

template<typename T,int size>
class Container
{
public:
	void display();
private:
	T m_obj;
};
template<typename T,int size>
void Container<T,size>::display()
{
	for(int i=0;i<size;i++)
	cout<<m_obj<<endl;
}
//使用
Container<int,5> con;
con.display();

特别提醒:模版代码不能分离编译,也就是说不能写成.h文件和.cpp文件分开,所有的代码都要写在.h文件中。

7. 标准模版库STL

7.1向量 vector

vector本质是对数组的封装,读取能在常数时间内完成。

1. 初始化方式
在这里插入图片描述
2. 常用函数
在这里插入图片描述
以遍历方法为例:

vector<int> v;
v.push_back(1);
for(int i=0;i<v.size();i++)
	cout<<v[i]<<endl;
//或者使用迭代器
vector<int>::iterator it=v.begin();//::标记当前的迭代器是属于向量的迭代器
for(;it!=v.end();it++)//end()表示向量最后一个元素的下一个元素,++表示指向向量的下一个元素
	cout<<*it<<endl;//*it表示迭代器指向的元素本身

7.2 链表list

链表数据插入速度快 ,使用方法与向量基本相同,push,insert,begin,end

7.3 映射map

map<int,string> m;
pair<int,string> p1(10,"p1");
m.insert(p1);
m.insert(make_pair(20,"p2"));
cout<<m[10]<<endl;

整理自慕课网c++远征

猜你喜欢

转载自blog.csdn.net/weixin_43927408/article/details/88614151