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的成员。
友元的注意事项:
- 友元关系不可传递
C是B的友元,B是A的友元,但C不一定是A的友元,反之亦然。 - 友元关系是单向的
B是A的友元,A不一定是B的友元 - 友元声明的形式及数量不受限制
- 友元只是封装的补充,破坏了封装性,应尽量避免使用
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指针,因此并不知道访问的是哪个对象的数据成员,所以不能访问非静态成员。但静态成员函数可以访问静态成员。
静态使用注意事项:
- 静态数据成员必须单独初始化,不能写到构造函数中
- 静态成员函数不能调用非静态成员函数和非静态数据成员
- 静态数据成员只有一份,不依赖于对象而存在。如果用sizeof求对象大小,是不包括静态数据成员的
4. 运算符重载
运算符重载的本质就是函数重载。关键字operator,包括一元运算符重载(-负号,++自增),只与一个操作数进行运算,二元运算符重载
一元运算符重载:
有两种方式,友元函数重载和成员函数重载。
友元函数重载中传的参数分别是操作符左边的数,操作符右边的数,成员函数重载传入的参数是操作符右边的数,当前对象调用
4.1 一元运算符重载
4.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-()
- 友元函数重载
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 重载++
- 前置++
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++()
- 后置++
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 重载+
- 成员函数重载
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)
- 友元函数重载
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++远征