Skip to content

Latest commit

 

History

History
504 lines (372 loc) · 12.8 KB

C++031:C++模板初探:C++的模板与友元:深入解析.md

File metadata and controls

504 lines (372 loc) · 12.8 KB
title categories tags
C++ 模板与友元:深入解析
开发
cpp
c++
cpp

C++ 模板与友元:深入解析

在C++中,模板(Template)和友元(Friend)是两个非常强大的特性。模板允许我们编写通用的代码,而友元则允许我们在类的外部访问类的私有成员。当这两个特性结合在一起时,可以实现非常灵活且强大的功能。本文将按照不同的场景,详细讲解模板与友元的各种结合方式。

1. 类友元

类友元是指将一个类声明为另一个类的友元。类友元可以分为以下几种情况:

1.1 普通类的类模板友元

1.1.1 一对一关系:将类模板的一个实例声明为普通类的友元

在这种关系中,普通类只将类模板的一个特定实例声明为友元。

#include <iostream>

template <typename T>
class MyTemplateClass;
// 普通类
class MyClass {
private:
    int data;

public:
    MyClass(int d) : data(d) {}

    // 将类模板的一个实例声明为友元
    friend class MyTemplateClass<int>;
};

// 类模板
template <typename T>
class MyTemplateClass {
public:
    void printData(const MyClass& obj) {
        std::cout << "Data: " << obj.data << std::endl;  // 访问MyClass的私有成员
    }
};

int main() {
    MyClass obj(42);
    MyTemplateClass<int> templateObj;
    templateObj.printData(obj);  // 输出: Data: 42

    return 0;
}

代码分析

  • MyClass 是一个普通类,它将 MyTemplateClass<int> 声明为友元。
  • MyTemplateClass<int>MyTemplateClass 模板的一个实例,它可以访问 MyClass 的私有成员 data

1.1.2 一对多关系:将类模板声明为类的友元模板

在这种关系中,普通类将整个类模板声明为友元,这意味着类模板的所有实例都是该类的友元。

#include <iostream>

// 普通类
class MyClass {
private:
    int data;

public:
    MyClass(int d) : data(d) {}

    // 将类模板声明为友元模板
    template <typename T>
    friend class MyTemplateClass;
};

// 类模板
template <typename T>
class MyTemplateClass {
public:
    void printData(const MyClass& obj) {
        std::cout << "Data: " << obj.data << std::endl;  // 访问MyClass的私有成员
    }
};

int main() {
    MyClass obj(42);
    MyTemplateClass<int> templateObj1;
    MyTemplateClass<double> templateObj2;

    templateObj1.printData(obj);  // 输出: Data: 42
    templateObj2.printData(obj);  // 输出: Data: 42

    return 0;
}

代码分析

  • MyClassMyTemplateClass 模板声明为友元模板,这意味着 MyTemplateClass 的所有实例(如 MyTemplateClass<int>MyTemplateClass<double>)都可以访问 MyClass 的私有成员。

1.2 类模板的普通类友元

在这种关系中,类模板将一个普通类声明为友元,这样,这个普通类就是类模板的所有实例的友元。

#include <iostream>

// 前置声明类模板
template <typename T>
class MyTemplateClass;

// 普通类
class MyFriendClass {
public:
    void printData(const MyTemplateClass<int>& obj);
};

// 类模板
template <typename T>
class MyTemplateClass {
private:
    T data;

public:
    MyTemplateClass(T d) : data(d) {}

    // 将普通类声明为友元
    friend class MyFriendClass;
};

// 实现 printData 函数
void MyFriendClass::printData(const MyTemplateClass<int>& obj) {
    std::cout << "Data: " << obj.data << std::endl;  // 访问MyTemplateClass的私有成员
}

int main() {
    MyTemplateClass<int> obj(42);
    MyFriendClass friendObj;
    friendObj.printData(obj);  // 输出: Data: 42

    return 0;
}

代码分析

  • MyTemplateClass 是一个类模板,它将 MyFriendClass 声明为友元。
  • MyFriendClass 可以访问 MyTemplateClass 的所有实例的私有成员 data

1.3 类模板的类模板友元

1.3.1 一对一关系:通过共享类型参数T,在类模板与类模板友元之间达成一对一的关系

在这种关系中,类模板和类模板友元共享相同的类型参数 T,从而形成一对一的关系。

#include <iostream>
template <typename U>
class MyTemplateFriendClass;
// 类模板
template <typename T>
class MyTemplateClass {
private:
    T data;

public:
    MyTemplateClass(T d) : data(d) {}

    // 将类模板友元声明为友元
    friend class MyTemplateFriendClass<T>;
};

// 类模板友元
template <typename U>
class MyTemplateFriendClass {
public:
    void printData(const MyTemplateClass<U>& obj) {
        std::cout << "Data: " << obj.data << std::endl;  // 访问MyTemplateClass的私有成员
    }
};

int main() {
    MyTemplateClass<int> obj(42);
    MyTemplateFriendClass<int> friendObj;
    friendObj.printData(obj);  // 输出: Data: 42

    return 0;
}

代码分析

  • MyTemplateClass<T>MyTemplateFriendClass<U> 共享相同的类型参数 T,从而形成一对一的关系。
  • MyTemplateFriendClass<T> 可以访问 MyTemplateClass<T> 的私有成员 data

1.3.2 多对多关系:为类模板友元声明一个不同的类型参数,可以达到多对多关系

在这种关系中,类模板友元使用不同的类型参数,从而形成多对多的关系。

#include <iostream>

// 类模板
template <typename T>
class MyTemplateClass {
private:
    T data;

public:
    MyTemplateClass(T d) : data(d) {}

    // 将类模板友元声明为友元
    template <typename U>
    friend class MyTemplateFriendClass;
};

// 类模板友元
template <typename U>
class MyTemplateFriendClass {
public:
    void printData(const MyTemplateClass<U>& obj) {
        std::cout << "Data: " << obj.data << std::endl;  // 访问MyTemplateClass的私有成员
    }
};

int main() {
    MyTemplateClass<int> obj1(42);
    MyTemplateClass<double> obj2(3.14);

    MyTemplateFriendClass<int> friendObj1;
    MyTemplateFriendClass<double> friendObj2;

    friendObj1.printData(obj1);  // 输出: Data: 42
    friendObj2.printData(obj2);  // 输出: Data: 3.14

    return 0;
}

代码分析

  • MyTemplateClass<T>MyTemplateFriendClass<U> 声明为友元,其中 U 是不同的类型参数。
  • 这意味着 MyTemplateFriendClass<U> 的所有实例都可以访问 MyTemplateClass<T> 的所有实例的私有成员 data

2. 函数友元

函数友元是指将一个函数声明为类的友元。函数友元可以分为以下几种情况:

2.1 普通类的函数模板友元

2.1.1 一对一关系:将函数模板的一个实例声明为普通类的友元

在这种关系中,普通类只将函数模板的一个特定实例声明为友元。

#include <iostream>
class MyClass;
template <typename T>
void printData(const MyClass& obj);
// 普通类
class MyClass {
private:
    int data;

public:
    MyClass(int d) : data(d) {}

    // 将函数模板的一个实例声明为友元
    friend void printData<int>(const MyClass& obj);
};

// 函数模板
template <typename T>
void printData(const MyClass& obj) {
    std::cout << "Data: " << obj.data << std::endl;  // 访问MyClass的私有成员
}

int main() {
    MyClass obj(42);
    printData<int>(obj);  // 输出: Data: 42

    return 0;
}

代码分析

  • MyClassprintData<int> 声明为友元。
  • printData<int> 可以访问 MyClass 的私有成员 data

2.1.2 一对多关系:将函数模板声明为类的友元模板

在这种关系中,普通类将整个函数模板声明为友元,这意味着函数模板的所有实例都是该类的友元。

#include <iostream>

// 普通类
class MyClass {
private:
    int data;

public:
    MyClass(int d) : data(d) {}

    // 将函数模板声明为友元模板
    template <typename T>
    friend void printData(const MyClass& obj);
};

// 函数模板
template <typename T>
void printData(const MyClass& obj) {
    std::cout << "Data: " << obj.data << std::endl;  // 访问MyClass的私有成员
}

int main() {
    MyClass obj(42);
    printData<int>(obj);  // 输出: Data: 42
    printData<double>(obj);  // 输出: Data: 42

    return 0;
}

代码分析

  • MyClassprintData 函数模板声明为友元模板,这意味着 printData 的所有实例都可以访问 MyClass 的私有成员。

2.2 类模板的函数友元

在这种关系中,类模板将一个普通函数声明为友元,这样,这个普通函数就是类模板的所有实例的友元。

#include <iostream>

// 类模板
template <typename T>
class MyTemplateClass {
private:
    T data;

public:
    MyTemplateClass(T d) : data(d) {}

    // 将普通函数声明为友元
    friend void printData(const MyTemplateClass<T>& obj);
};

// 普通函数
void printData(const MyTemplateClass<int>& obj) {
    std::cout << "Data: " << obj.data << std::endl;  // 访问MyTemplateClass的私有成员
}

int main() {
    MyTemplateClass<int> obj(42);
    printData(obj);  // 输出: Data: 42

    return 0;
}

代码分析

  • MyTemplateClass 是一个类模板,它将 printData 函数声明为友元。
  • printData 函数可以访问 MyTemplateClass 的所有实例的私有成员 data

2.3 类模板的函数模板友元

2.3.1 一对一关系:通过共享类型参数T,在类模板与函数模板友元之间达成一对一的关系

在这种关系中,类模板和函数模板友元共享相同的类型参数 T,从而形成一对一的关系。

#include <iostream>

// 类模板
template <typename T>
class MyTemplateClass {
private:
    T data;

public:
    MyTemplateClass(T d) : data(d) {}

    // 将函数模板的一个实例声明为友元
    friend void printData<T>(const MyTemplateClass<T>& obj);
};

// 函数模板友元
template <typename U>
void printData(const MyTemplateClass<U>& obj) {
    std::cout << "Data: " << obj.data << std::endl;  // 访问MyTemplateClass的私有成员
}

int main() {
    MyTemplateClass<int> obj(42);
    printData(obj);  // 输出: Data: 42

    return 0;
}

代码分析

  • MyTemplateClass<T>printData<U> 共享相同的类型参数 T,从而形成一对一的关系。
  • printData<T> 可以访问 MyTemplateClass<T> 的私有成员 data

2.3.2 多对多关系:为函数模板友元声明一个不同的类型参数,可以达到多对多关系

在这种关系中,函数模板友元使用不同的类型参数,从而形成多对多的关系。

#include <iostream>

// 类模板
template <typename T>
class MyTemplateClass {
private:
    T data;

public:
    MyTemplateClass(T d) : data(d) {}

    // 将函数模板友元声明为友元
    template <typename U>
    friend void printData(const MyTemplateClass<U>& obj);
};

// 函数模板友元
template <typename U>
void printData(const MyTemplateClass<U>& obj) {
    std::cout << "Data: " << obj.data << std::endl;  // 访问MyTemplateClass的私有成员
}

int main() {
    MyTemplateClass<int> obj1(42);
    MyTemplateClass<double> obj2(3.14);

    printData(obj1);  // 输出: Data: 42
    printData(obj2);  // 输出: Data: 3.14

    return 0;
}

代码分析

  • MyTemplateClass<T>printData<U> 声明为友元,其中 U 是不同的类型参数。
  • 这意味着 printData<U> 的所有实例都可以访问 MyTemplateClass<T> 的所有实例的私有成员 data

3. 模板参数友元

我们还可以将模板类型参数声明为友元。这意味着模板类型参数本身可以访问类的私有成员。

#include <iostream>
template <typename T>
class MyClass {
    // 将模板类型参数 T 声明为友元
    friend T;

private:
    int secret;

public:
    MyClass(int s) : secret(s) {}
};

// 定义一个类,它将访问 MyClass<T>::secret
class MyFriendClass {
public:
    void accessSecret(MyClass<MyFriendClass>& obj) {
        // 由于 MyFriendClass 被声明为 MyClass<MyFriendClass> 的友元
        // 因此它可以访问 secret
        std::cout << "Secret: " << obj.secret << std::endl;
    }
};

int main() {
    MyClass<MyFriendClass> myObj(42);
    MyFriendClass friendObj;
    friendObj.accessSecret(myObj);
    return 0;
}

代码分析

  • MyTemplateClass<T> 将模板类型参数 T 声明为友元。
  • 这意味着 T 类型的对象可以访问 MyTemplateClass<T> 的私有成员 data

4. 总结

本文详细讲解了C++中模板与友元的各种结合方式,包括类友元和函数友元的不同场景。通过这些结合方式,我们可以实现非常灵活且强大的代码设计。希望本文能帮助你更好地理解C++中的模板与友元机制,并在实际编程中灵活运用。

文章合集:chongzicbo/ReadWriteThink: 博学而笃志,切问而近思 (github.com)

个人博客:程博仕

微信公众号:

微信公众号