C++ - 友元和模版

友元:

  1. 在程序里,有些私有属性也想让类外特殊的一些函数或者类进行访问,就需要用到友元。
  2. 友元的目的就是让一个函数或者类访问另一个类中私有成员。
  3. 友元的关键字为:friend
  4. 友元的三种实现:
    • 全局函数做友元
    • 类做友元
    • 成员函数做友元

全局函数做友元:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<iostream>
using namespace std;
#include<string>
//全局函数做友元
class Building
{
//goodGay全局函数,前面加friend 可以访问Building的私有属性
friend void goodGay(Building *building);//写在类里面,也想当是声明
public:
Building()
{
m_SittingRoom="客厅";
m_BedRoom="卧室";
}
string m_SittingRoom;
private:
string m_BedRoom;
};
//全局函数
void goodGay(Building *building)
{
cout<<"全局函数正在访问:公共的属性"<<building->m_SittingRoom<<endl;
cout<<"全局函数正在访问:私有的属性"<<building->m_BedRoom<<endl;
}
void test01()
{
Building building;
goodGay(&building);
}
int main()
{
test01();
system("pause");
return 0;
}

类做友元:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include<iostream>
using namespace std;
#include<string>
//类做友元
class Building
class GoodGay
{
public:
GoodGay();
void visit();//参观函数 访问Building中的属性
Building *building;
};
class Building
{
//GoodGay类前面加friend 可以访问Building类中的私有属性
friend class GoodGay;//核心
public:
Building();
string m_SittingRoom;//客厅
private:
string m_BedRoom;//卧室
};
//类外写成员函数(类里面需要声明函数)
Building::Building()//表示在Building作用域下的构造函数
{
m_SittingRoom="客厅";
m_BedRoom="卧室";
}
GoodGay::GoodGay()//表示在GoodGay作用域下的构造函数
{
//创建建筑物对象
building=new Building;//相当于创建一个对象在堆区,并用指针指针接收这个对象,也就说building包含了Building类的地址,所以building里面包含了Building类的所有东西。
}
void GoodGay::visit()//表示在GoodGay作用域下的成员函数
{
cout<<"GoodGay类正在访问:公共属性"<<building->m_SittingRoom<<endl;
cout<<"GoodGay类正在访问:私有属性"<<building->m_BedRoom<<endl;
}
void test01()
{
GoodGay gg;
gg.visit();
}
int main()
{
test01();
system("pause");
return 0;
}

成员函数做友元:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<iostream>
using namespace std;
#include<string>
class Building;
class GoodGay
{
public:
GoodGay();
void visit();//让visit函数可以访问Building中的私有成员
void visit2();//让visit2函数不可以访问Building中的私有成员
Building *building;
};
class Building
{
//告诉编译器 GoodGay类下的visit成员函数作为本类的友元,可以访问私有成员
friend void GoodGay::visit();//核心
public:
Building();
string m_SittingRoom;//客厅
private:
string m_BedRoom;//卧室
};
//类外写成员函数(类里面需要声明函数)
Building::Building()//表示在Building作用域下的构造函数
{
m_SittingRoom="客厅";
m_BedRoom="卧室";
}
GoodGay::GoodGay()//表示在GoodGay作用域下的构造函数
{
//创建建筑物对象
building=new Building;//相当于创建一个对象在堆区,并用指针指针接收这个对象,也就说building包含了Building类的地址,所以building里面包含了Building类的所有东西。
}
void GoodGay::visit()//表示在GoodGay作用域下的成员函数
{
cout<<"visit 正在访问:公共属性"<<building->m_SittingRoom<<endl;
cout<<"visit 正在访问:私有属性"<<building->m_BedRoom<<endl;
}
void GoodGay::visit2()
{
cout<<"visit2 正在访问:公共属性"<<building->m_SittingRoom<<endl;
//cout<<"visit2 正在访问:私有属性"<<building->m_BedRoom<<endl;//不能访问私有属性
}
void test01()
{
GoodGay gg;
gg.visit();
gg.visit2();
}
int main()
{
test01();
system("pause");
return 0;
}

关于友元:

  1. 友元的关系是单向的而不是双向的

    如果声明了类 B 是类 A 的友元类,不等于类A 是类 B 的友元类,类 A 中的成员函数不能访问类 B 中的 private 成员。

  2. 友元的关系不能传递

    如果类 B 是类 A 的友元类,类 C 是类 B 的友元类,不等于类 C 是类 A 的友元类。

  3. 除非有必要一般不建议把整个类声明为友元类,而只将某些成员函数声明为友元函数,这样更安全一些。

泛型程序设计:

  1. 泛型程序设计(generic programming)是一种算法在实现时不指定具体要操作的数据的类型的程序设计方法。
  2. 所谓“泛型”,指的是算法只要实现一遍,就能适用于多种数据类型。泛型程序设计方法的优势在于能够减少重复代码的编写。
  3. 在 C++ 中,模板分为函数模板和类模板两种。在编写函数时会考虑能否将其写成函数模板,编写类时会考虑能否将其写成类模板,以便实现重用。

函数模板:

  1. 模板函数将数据的类型通过参数来传递,在函数定义时不指明具体的数据类型,当发生函数调用时,编译器可以根据传入的实参自动推断数据类型, 这就是类型的参数化。(值(Value)和类型(Type)是数据的两个主要特征,它们在 C++中都可以被参数化。)

  2. 所谓函数模板,实际上是建立一个通用函数,它所用到的数据的类型(包括返回值类型、形参类型、局部变量类型)可以不具体指定,而是用一个虚拟的类型来代替(实际上是用一个标识符来占位),等发生函数调用时再根据传入的实参来逆推出真正的类型。这个通用函数就称为函数模板(Function Template)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #include <iostream>
    using namespace std;

    //函数模版
    template<typename T> void Swap(T *a, T *b){
    T temp = *a;
    *a = *b;
    *b = temp;
    }

    int main(){
    //交换 int 变量的值
    int n1 = 100, n2 = 200;
    Swap(&n1, &n2);
    cout<<n1<<", "<<n2<<endl;
    //交换 float 变量的值
    float f1 = 12.5, f2 = 56.93;
    Swap(&f1, &f2);
    cout<<f1<<", "<<f2<<endl;
    //交换 char 变量的值
    char c1 = 'A', c2 = 'B';
    Swap(&c1, &c2);
    cout<<c1<<", "<<c2<<endl;
    //交换 bool 变量的值
    bool b1 = false, b2 = true;Swap(&b1, &b2);
    cout<<b1<<", "<<b2<<endl;
    return 0;
    }
  3. 当模板函数与普通函数重载时,系统将会优先调用普通函数如果必须要使用模板函数,在函数调用时加<>声明

    • Swap <> (&b1, &b2);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <iostream>
    using namespace std;
    //普通全局函数
    void Swap(int *a, int *b){
    cout << "Swap(a,b) : " << *a << ","<< *b << endl;
    }
    //函数模版
    template<typename T> void Swap(T *a, T *b){
    T temp = *a;
    *a = *b;
    *b = temp;
    }

    int main(){
    //交换 int 变量的值
    int n1 = 100, n2 = 200;
    Swap(&n1, &n2);//调用的是普通全局函数普通全局函数
    Swap<>(&n1, &n2);//调用的是函数模版
    cout<<n1<<", "<<n2<<endl;
    return 0;
    }

类模版:

  1. 函数模板中定义的类型参数可以用在函数声明和函数定义中,类模板中定义的类型参数可以用在类声明和类实现中

  2. 类模板的目的同样是将数据的类型参数化

  3. 声明类模板的语法为:

    1
    2
    3
    4
    template <typename T>  // 或者 template <class T>
    class ClassName {
    // 类成员定义
    };
  4. 类模板不是用来创建一个全新的泛型类,而是通过参数化类中的特定类型来生成一系列相似但类型不同的具体类。它允许我们定义一个通用的类蓝图,其中某些成员变量类型、成员函数参数类型或返回类型可以被参数化,从而在编译时根据指定的类型参数生成具体的类实例

  5. 比如有两个类,所有的方法与属性都一样,只是有个属性参数类型不一样,那么使用泛类型+模板类实现只要一个类即可:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class A{
    int a;
    void fan(int a){}
    }
    class B{
    double a;
    void fan(double a){}
    }

    template <class T>
    class Temp{
    T a;
    void fan(T a){}
    }
    //模板头和类头是一个整体,可以换行,但是中间不能有分号。
    //之后我们便有了 N 个类:
    Temp<int>
    Temp<double>
  6. 实例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #include <iostream>
    using namespace std;

    template<class T1, class T2> //这里不能有分号
    class Point{

    public:
    //构造函数
    Point(T1 x, T2 y): m_x(x), m_y(y){}
    T1 getX(); //获取 x 坐标
    T2 getY(); //获取 y 坐标

    private:
    T1 m_x; //x 坐标
    T2 m_y; //y 坐标
    };

    template<class T1, class T2> //模板头
    T1 Point<T1, T2>::getX() /*函数头*/ {
    return m_x;
    }

    template<class T1, class T2>
    T2 Point<T1, T2>::getY(){
    return m_y;
    }

    /*
    当类模板后,我们将拥有非常多新的类:
    Point<int,int>
    Point<int,char>
    Point<char,int>
    Point<float,int>Point<int,float>
    Point<bool,int>
    Point<int,bool>
    ...
    */
    int main(){
    Point<int, int> p1(10, 20);
    cout<<"x="<<p1.getX()<<", y="<<p1.getY()<<endl;
    Point<int, string> p2(10, "东经 180 度");
    cout<<"x="<<p2.getX()<<", y="<<p2.getY()<<endl;
    Point<string, string> *p3 = new Point<string, string>("东经 180 度", "北纬 210 度");
    cout<<"x="<<p3->getX()<<", y="<<p3->getY()<<endl;
    delete p3;
    return 0;
    }

    模版类

参考链接: