C++05模板

C++模板

友元函数

关键字:friend

友元全局函数:将全局函数声明为友元函数就是友元全局函数,可以访问到私有数据成员和私有成员函数。friend void printTime(Time &t);

#include<iostream>` `using namespace std;` `class Time{` `friend void printTime(Time &t);` `public:` `Time(int hour,int minute,int second);` `private:` `int m_iHour;` `int m_iMinute;` `int m_iSecond;`
};

==========================================

#include "Time.h"
Time::Time(int hour,int minute,int second){
m_iHour=hour;
m_iMinute=minute;
m_iSecond=second;
}

=========================================

#include<iostream>
#include"Time.h"
using namespace std;
void printTime(Time &t){
cout<<t.m_iHour<<":"<< t.m_iMinute<<":"<<t.m_iSecond<<endl;
}
int main(int argc, char *argv[]) {
Time t(6,34,12);
printTime(t);
}

友元成员函数:将成员函数声明为友元函数就是友元成员函数friend void Match::printTime(Time &t);

#ifndef T_Time
#define T_Time
#include<iostream>
#include"Match.h"
using namespace std;
class Time{
friend void Match::printTime(Time &t);
public:
Time(int hour,int minute,int second);
private:
int m_iHour;
int m_iMinute;
int m_iSecond;
``
};
#endif

======================

#ifndef M_match
#define M_match
class Time;
class Match{
public:
void printTime(Time &t);
};
#endif

==========================

#include<iostream>
#include "Match.h"
#include"Time.h"
using namespace std;
void Match::printTime(Time &t){
cout<<t.m_iHour<<":"<< t.m_iMinute<<":"<<t.m_iSecond<<endl;

}

=======================

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

int main() {
Time t(6,34,12);
Match m;
m.printTime(t);
}

遇到的问题:由于多个文件引用同一个头文件时,没有使用#ifndef和#endif就出现了redefinition的问题

image-20201119201126392

解决办法:

文件中的#ifndef
头件的中的#ifndef,这是一个很关键的东西。比如你有两个C文件,这两个C文件都include了同一个头文件。而编译时,这两个C文件要一同编译成一个可运行文件,于是问题来了,大量的声明冲突。
还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:
#ifndef <标识>
#define <标识>
......
#endif
<标识>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h
#ifndef STDIO_H
#define STDIO_H
......
#endif

友元类

关键字:friend

声明了某个函数的友元类之后,此友元类可以访问到该类的所有属性和方法。

友元关系不可传递。

友元关系的单向性。

友元声明的形式(友元类/友元函数)以及数量(一个类可以有多个友元类)不受限制。

友元只是封装的补充。破坏了封装性,定向暴露,不建议使用。

注意:

1、在做练习题的时候有一个错误地方,就是构造函数的定义要加{};声明是不需要。

2、在A类中声明friend class B的意思是,B是A的友元类,B可以访问A的成员函数和属性。

static

在数据成员和成员函数之前加上static就变成了静态数据成员和静态成员函数。

普通数据成员和静态数据成员:

静态的依赖类。静态成员函数中,并不传入this指针,无法调用非静态数据成员,同时也不可以加上const关键字,但是可以调用静态成员或者全局成员。

注意事项:

静态数据成员必须单独初始化(在构造器之外),因为它并不随着对象的产生而产生,而是随着类的产生而产生。

静态成员函数不能调用非静态成员函数和非静态数据成员,后者可以调用前者。

运算符重载

给原有的运算符赋予新的意义。

成员函数的运算符重载:

#include<iostream>` `using namespace std;` `class Coor{` `public:` `Coor(int x,int y);` `Coor &operator-();` `int getX();` `int getY();` `private:` `int m_iX;` `int m_iY;` `};

=========================

#include"Coor.h"
Coor::Coor(int x,int y){
m_iX=x;
m_iY=y;
}
int Coor::getX(){
return m_iX;
}
int Coor::getY(){
return m_iY;
}
Coor &Coor::operator-(){
this->m_iX=-this->m_iX;
this->m_iY=-this->m_iY;
return *this;
}

=============================

#include"Coor.h"
#include<iostream>
using namespace std;
int main(){
Coor c(1,2);
cout<<c.getX()<<","<<c.getY()<<endl;
-c;
cout<<c.getX()<<","<<c.getY()<<endl;
}

友元函数的运算符重载:

	#include<iostream>
using namespace std;
class Coor{
	friend Coor &operator-(Coor &c);
	public:
		Coor(int x,int y);
	int getX();
	int getY();
private:
	int m_iX;
	int m_iY;
}; 

=======================

#include"Coor.h"
Coor::Coor(int x,int y){
m_iX=x;
m_iY=y;
}
int Coor::getX(){
return m_iX;
}
int Coor::getY(){
return m_iY;
}
Coor &operator-(Coor &c){
c.m_iX= -c.m_iX;
c.m_iY= -c.m_iY;
return c;
}

============================

#include"Coor.h"
#include<iostream>
using namespace std;
int main(){
Coor c(1,2);
cout<<c.getX()<<","<<c.getY()<<endl;
-c;
cout<<c.getX()<<","<<c.getY()<<endl; return 0;
}

++运算符重载,前置++和后置++用(int)来区分,带(int)是后置。

** 二元运算符的重载**

+号运算符可以用成员函数也可以用友元函数重载。

输出运算符''<<''不可以使用成员函数重载,必须使用友元函数重载。因为第一个对象必须是ostream对象。

索引运算符''[]''不可以使用友元函数重载,必须使用成员函数重载。因为第一个必须是该类自身的对象。

练习:

#include <iostream>
using namespace std;
/**
 * 定义Coordinate类
 * 数据成员:m_iX,m_iY
 * 成员函数:构造函数
 * 重载--运算符,重载+运算符
   */
   class Coordinate
   {
   public:
   Coordinate(int x, int y)
   {
   	m_iX = x;
   	m_iY = y;
   }
   // 前置--运算符重载
   Coordinate &operator--(){
       m_iX--;
       m_iY--;
   }
   // 后置--运算符重载
   Coordinate &operator--(int){
        m_iX--;
        m_iY--;
   }
   // +号运算符重载
   Coordinate operator+(Coordinate c1){
        Coordinate temp(0,0);
        temp.m_iX=this->m_iX+c1.m_iX;
        temp.m_iY=this->m_iY+c1.m_iY;
        return temp;
   }
public:
	int m_iX;
	int m_iY;
};
int main(void)
{
	Coordinate coor1(1, 3);
	Coordinate coor2(2, 4);
	Coordinate coor3(0, 0);
coor1--;
--coor2;
coor3 = coor1 + coor2;
cout << coor3.m_iX << endl;
cout << coor3.m_iY << endl;
return 0;
}

模板函数和模板类

模板函数

将类型作为参数。

关键字:template、typename、class

函数模板:

template <typename T,class C>

T max(T a,T b){

return (a>b)>a:b;}

通过函数模板产生的函数时模板函数。

函数模板与重载:函数模板只有在使用的时候,产生出来的函数之间才是重载的关系。

#include<iostream>
using namespace std;
template <typename T>
void display(T a){
cout<<a<<endl;
}
template <typename T,class S>
void display(T t, S s){
cout<<t<<endl;
cout<<s<<endl;
}
template <typename T ,int size>
void display(T a){
for(int i=0;i<size;i++){
cout<<a<<endl;
}
}
int main(){
display<int>(10);
display<double>(12.2);

display<int,double>(2,33.3);

display<int,10>(7);
return 0;
}

函数模板参数个数如果为0个,则没有必要使用函数模板

练习:

#include <iostream>
using namespace std;

/**

  • 定义模板函数swapNum
  • 实现功能:交换两个数的位置
    */
    template <typename T,typename C>
    void swap(T a,T b)
    {
    T temp = a;
    a = b;
    b = temp;
    }

int main(void)
{
int x = 10;
int y = 20;
// 调用模板函数
swap<int,int>(x,y);
cout << "x = " << x << endl;
cout << "y = " << y << endl;
return 0;
}

类模板

只有数据类型不同。

关键字:template 、class

template

class M{

public:

...

void display();//在有类模板的条件下,类内的成员函数的定义每有什么不一样。

private:

T *m_pArr;//类内数据类型。

};

当成员函数在类外定义的时候:

template

void M::display(){
}

模板代码不能分离编译。不能将声明和定义在.h和.cpp中分开写。必须都写在.h中。

练习:

#include <iostream>
using namespace std;

/**

  • 定义一个矩形类模板Rect
  • 成员函数:calcArea()、calePerimeter()
  • 数据成员:m_length、m_height
    */
    template<class T>
    class Rect
    {
    public:
    Rect(T length,T height);
    T calcArea();
    T calePerimeter();
    public:
    T m_length;
    T m_height;
    };

/**

  • 类属性赋值
    */
    template<typename T>
    Rect<T>::Rect(T length, T height)
    {
    m_length = length;
    m_height = height;
    }

/**

  • 面积方法实现
    */
    template<typename T>
    T Rect<T>::calePerimeter()
    {
    return m_length * m_height;
    }

/**

  • 周长方法实现
    */
    template<typename T>
    T Rect<T>::calcArea()
    {
    return ( m_length + m_height) * 2;
    }

int main(void)
{
Rect<int> rect(3, 6);
cout << rect.calcArea() << endl;
cout << rect.calePerimeter() << endl;
return 0;
}

标准模板类

C++标准模板库:STL(Standard Template Lib)

vector向量

本质时对数组的封装。vector的大小可以根据元素数量改变。

1、初始化:

image-20201120154925558

2、常用方法:

image-20201120155003630

3、例子:

#include<iostream>
#include<vector>
#include<list>
#include<map>
using namespace std;
int main(){
vector<int> vec;
vec.push_back(1);
vec.push_back(3);
vec.push_back(4);
for(int i=0;i<vec.size();i++){
cout<<vec[i]<<endl;
}
vector<int>::iterator itor=vec.begin();
for(;itor!=vec.end();itor++){
cout<<*itor<<endl;
}
cout<<vec.front()<<endl;
cout<<vec.back()<<endl;
vec.pop_back();
cout<<vec.size()<<endl;
return 0;
}

4、向量的遍历:for循环像遍历数组那样;或者使用迭代器iterator

list链表

list[i]不是实现,所以list的遍历可以用迭代器来实现。

#include<iostream>
#include<vector>
#include<list>
#include<map>
using namespace std;
int main(){
list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.push_back(4);
list<int>::iterator i=l.begin();
for(;i!=l.end();i++){
cout<<*i<<endl;
}
return 0;
}

map映射

image-20201120155810722

map中没有push_back,使用的时insert。使用迭代器的时候,因为是键值对,所以要分别输出key和value

#include<iostream>
#include<vector>
#include<list>
#include<map>
#include<string>
using namespace std;
int main(){
map<int,string> m;
pair<int,string>p1(1,"GOT7");
pair<int,string>p2(2,"Hello");
m.insert(p1);
m.insert(p2);
for(int i=0;i<m.size();i++){
cout<<m[i]<<endl;
}
map<int,string>::iterator it=m.begin();
for(;it!=m.end();it++){
cout<<it->first<<endl;
cout<<it->second<<endl;
}
return 0;
}

练习:

#include <vector>
#include <map>
#include <string>
#include <iostream>
using namespace std;

int main(void)
{
    // 使用vector存储数字:3、4、8、4
    vector<int> vec;
    vec.push_back(3);
    vec.push_back(4);
    vec.push_back(8);
    vec.push_back(4);
//循环打印数字
for(int i=0;i<vec.size();i++){
    cout<<vec[i]<<endl;
}

// 使用map来存储字符串键值对
map<string, string> m;
pair<string,string>p1("S","Shang Hai");
pair<string,string>p2("B","Bei Jing");
pair<string,string>p3("G","Guang Zhou");
   m.insert(p1);
   m.insert(p2);
   m.insert(p3);

    // 打印map中数据
    
    map<string,string>::iterator it=m.begin();
    for(;it!=m.end();it++){
        cout<<it->first<<endl;
        cout<<it->second<<endl;
    }
    return 0;
    }

当map的key和value都是string类型的时候,好像只可以使用iterator来遍历。使用cout<<m[i]<<endl;是不可以遍历出来的。

原文地址:https://www.cnblogs.com/yunxiaoqian/p/14014646.html