美文网首页
15.C++泛型

15.C++泛型

作者: 任振铭 | 来源:发表于2019-08-21 08:12 被阅读0次

模板函数

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include <algorithm>
#include <vector>

using namespace std;

template <typename T>
void myswap(T& t, T& t2) {
    T temp = t;
    t = t2;
    t2 = temp;
    cout << "t=" << t << " t2=" << t2 << endl;;
}
void method() {
    int a = 1, b = 2;
    myswap<int>(a, b);
    myswap(a, b);//自动类型推导

    char c = 'c';
    char d = 'd';
    myswap(c, d);

    float e = 1.2f;
    float f = 32.3f;
    myswap<float>(e, f);
}
void main() {
    
    method();
    system("pause");
}

模板类

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include <algorithm>
#include <vector>
using namespace std;

template <typename R>
class C {
public:
    C(R r) {
        this->r = r;
    }
    void printR() {
        cout<<r<<endl;
    }
public:
    R r;
};
void main() {
    
    C<int> c(12);
    c.printR();
    C<float> d(12.3f);
    d.printR();
    system("pause");
}

//模板类派生普通类


#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include <algorithm>
#include <vector>
using namespace std;

template <typename R>
class C {
public:
    C(R r) {
        this->r = r;
        cout << "C(R r) :" << r << endl;
    }
    void printR() {
        cout<<r<<endl;
    }
public:
    R r;
};

class B :public C<int> {
public:
    B(int c) :C<int>(c){
        cout <<"B(int c) :"<< c << endl;
    }
    int b;
};
void main() {
    
    C<int> c(12);
    c.printR();
    C<float> d(12.3f);
    d.printR();

    B b(45);
    system("pause");
}

//模板类派生模板类


#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include <algorithm>
#include <vector>
using namespace std;

template <typename R>
class C {
public:
    C(R r) {
        this->r = r;
        cout << "C(R r) :" << r << endl;
    }
    void printR() {
        cout<<r<<endl;
    }
public:
    R r;
};

template <typename Z>
class B :public C<Z> {
public:
    B(Z c) :C<Z>(c){
        cout <<"B(int c) :"<< c << endl;
    }
    Z b;
};
void main() {
    B<int> b(45);
    system("pause");
}

函数模板和友元函数

注意,只在重载左移右移运算符的时候使用友元函数,其他都不要用,友元函数容易导致问题

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include <algorithm>
#include <vector>
using namespace std;

template <typename T>
class Complex {
public:
    T a;
    T b;
    Complex(T a, T b) {
        this->a = a;
        this->b = b;
    }

    //友元函数如果函数体写在类外边,那么T拿不到,所以只能这样
    friend ostream& operator<<(ostream& o, Complex<T> c) {
        o << c.a << " " << c.b << endl;
        return o;
    }

    Complex& operator+(Complex &c) {
        Complex temp(a + c.a, b + c.b);
        return temp;
    }

    void print() {
        cout << a << " " << b << endl;
    }
};

void main() {
    Complex<int> c1(1, 2);
    Complex<int> c2(3, 4);
    Complex<int> c3 = c1 + c2;
    c3.print();
    cout << c3 << endl;
    system("pause");
}

传引用和传对象的区别

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include <algorithm>
#include <vector>
using namespace std;

template <typename T>
class Complex {
public:
    T a;
    T b;
    Complex(T a, T b) {
        this->a = a;
        this->b = b;
    }

    Complex(Complex& c) {
        a = c.a;
        b = c.b;
        cout << "拷贝构造函数执行 " <<a<<" "<<b<< endl;
    }
};
//传对象有一个赋值过程,会走拷贝构造函数,相当于
//Complex b(12,3);
//Complex c = b;
void method(Complex<int> c) {
    cout<<c.a<<" "<<c.b<<endl;
}

//传引用不会走拷贝构造函数
void method2(Complex<int> &c) {
    cout << c.a << " " << c.b << endl;
}

void main() {
    Complex<int> c1(1, 2);
    method(c1);
    system("pause");
}

函数声明写在类内部,实现写在类外部

编译通过,但是运行你会发现其他方法都可以,唯独友元函数这样实现报错
严重性 代码 说明 项目 文件 行 禁止显示状态
错误 LNK2019 无法解析的外部符号 "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Complex<int>)" (??6@YAAAV?basic_ostream@DU?char_traits@D@std@@@std@@AAV01@V?$Complex@H@@@Z),该符号在函数 _main 中被引用 Project1 C:\Users\Administrator\source\repos\Project1\Project1\helloworld.obj 1

需要在友元函数的声明后加上泛型T

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include <algorithm>
#include <vector>
using namespace std;

template <typename T>
class Complex {
public:
    T a;
    T b;
    Complex(T a, T b);
    //friend ostream& operator<<(ostream& o, Complex<T> c);//错误
        friend ostream& operator<<<T>(ostream& o, Complex<T> c);//正确
    Complex& operator+(Complex& c);
    void print();
};

template <typename T>
Complex<T>::Complex(T a, T b) {
    this->a = a;
    this->b = b;
}

template <typename T>
void Complex<T>::print() {
    cout << a << " " << b << endl;
}

template <typename T>
Complex<T>& Complex<T>::operator+(Complex<T>& c) {
    Complex<T> temp(a + c.a, b + c.b);
    return temp;
}

template <typename T>
ostream& operator<<(ostream& o, Complex<T> c) {
    o << c.a << " " << c.b << endl;
    return o;
}

void main() {
    Complex<int> c1(1, 2);
    Complex<int> c2(3, 4);
    Complex<int> c3 = c1 + c2;
    c3.print();
    cout << c3 << endl;
    system("pause");
}

类的声明在.h文件中,类的实现在.cpp文件中时,

test.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include <algorithm>
#include <vector>
using namespace std;
template <typename T>
class Complex {
public:
    T a;
    T b;
    Complex(T a, T b);
    friend ostream& operator<<<T>(ostream& o, Complex<T> c);
    Complex& operator+(Complex& c);
    void print();
};

test.cpp

#include"test.h"
template <typename T>
Complex<T>::Complex(T a, T b) {
    this->a = a;
    this->b = b;
}

template <typename T>
void Complex<T>::print() {
    cout << a << " " << b << endl;
}

template <typename T>
Complex<T>& Complex<T>::operator+(Complex<T>& c) {
    Complex<T> temp(a + c.a, b + c.b);
    return temp;
}

template <typename T>
ostream& operator<<(ostream& o, Complex<T> c) {
    o << c.a << " " << c.b << endl;
    return o;
}

main.cpp


//当你的类中使用了类模板,类的声明在.h文件中,类的实现在.cpp文件中时,
//引入类时通过#include "test.h"是不行的,会报错,正确的方式是引入cpp文件
//这也是为什么有些库中会出现hpp文件的原因,就是把声明和实现写在同一个类中
//#include "test.h"
#include "test.cpp"

void main() {
    Complex<int> c1(1, 2);
    Complex<int> c2(3, 4);
    Complex<int> c3 = c1 + c2;
    c3.print();
    cout << c3 << endl;
    system("pause");
}

模板函数中的static变量

static 属于整个程序,但是当在模板中使用,他就属于当前模板类型的对象,如下程序从打印结果可以看出,两个static是没有关联的


#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include "iostream"

using namespace std;
template <typename T>
class Complex {
public:
    static T c;
};

template <typename T>
T Complex<T>::c = 100;

void main() {
    Complex<int> c1;
    Complex<char> c2;
    c1.c++;
    c1.c++;
    c1.c++;
    c2.c++;
    c2.c++;
    c2.c++;
    cout<<c1.c<<endl;
    cout<<c2.c<<endl;
    system("pause");
}

打印结果
103
g
请按任意键继续. . .

使用类泛型模板实现数组操作,要求实现数组通用属性[],=,cout<<

头文件

#pragma once

#include <iostream>
using namespace std;

template <typename T>
class MyVector
{

public:
    MyVector(int size = 0);
    MyVector(const MyVector& v); 
    ~MyVector();

public:
    //[]
    T& operator[](int index);
    //=
    MyVector &operator=(MyVector& v);
    //<<
    friend ostream& operator<< <T>( ostream& cout,MyVector& v);

public:
    T* m_space;
    int m_len;
};

源文件

#include "MyVector.h"

template <typename T>
MyVector<T>::MyVector(int size) {
    m_space = new T[size];
    m_len = size;
}

template <typename T>
MyVector<T>::MyVector(const MyVector<T>& v) {
    m_len = v.m_len;
    m_space = new T[m_len];
    for (int i = 0; i < m_len; i++) {
        m_space[i] = v.m_space[i];
    }
}

template <typename T> 
MyVector<T>::~MyVector() {
    if (m_space != NULL) {
        delete[]m_space;
        m_space = NULL;
        m_len = 0;
    }
}

template <typename T>
T& MyVector<T>::operator[](int index) {
    return m_space[index];
}

template <typename T>
MyVector<T>& MyVector<T>::operator=(MyVector<T>& v) {
    //先释放原有内存
    if (m_space != NULL) {
        delete[]m_space;
        m_space = NULL;
        m_len = 0; 
    }

    //再分配新的内存
    m_len = v.m_len;
    m_space = new T[m_len];
    
    //拷贝数据
    for (int i = 0; i < m_len; i++) {
        m_space[i] = v[i];
    }
    return *this;
}

template <typename T>
ostream& operator<<(ostream& cout, MyVector<T>& v){
    for (int i = 0; i < v.m_len; i++) {
        cout << v.m_space[i] << " ";
    }
    cout << endl;
    return cout;
}

调用端


#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include "MyVector.cpp"

void main() {
    MyVector<int> vector(10);
    cout << "使用[]给数组赋值" << endl;
    for (int i = 0; i < vector.m_len; i++) {
        vector[i] = i + 1;
        cout << vector[i]<<" ";
    }

    cout<<endl;

    cout << "使用=号实现数组拷贝" << endl;
    MyVector<int> v2 = vector;
    for (int i = 0; i < v2.m_len; i++) {
        cout << v2[i]<<" ";
    }

    cout << endl;
    cout << "使用<<输出整个数组" << endl;
    cout << v2 << endl;

    system("pause");
}

数组保存对象

还是用上边的MyVector数组,添加自定义对象,这个对象要满足一些条件
a.实现拷贝构造函数,避免浅拷贝问题
b.重载等号=运算符,因为数组要通过arr[i] = obj赋值
c.重载<<运算符,因为cout<<arr应该可以打印数组元素
d.析构函数,保证数据正常销毁
e.有参构造和无参构造都要添加

拷贝构造函数和=号运算符的执行时机:
拷贝构造函数是在对象未分配内存之前通过=号赋值时执行
而重载=号运算符是在对象已经分配内存后,重新赋值时会执行,可以看main方法中的执行
虽然两者都使用了=号,但是执行时机不同,这也是为什么拷贝构造函数不需要释放内存的原因,因为他执行前并没有分配内存

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
#include "MyVector.cpp"

class Teacher {
public:
    int age;
    char* name;

    Teacher() {
        this->age = 0;
        name = new char[1];
        strcpy(name, "");

    }

    Teacher(int age, const char* n) {
        this->age = age;
        //注意:char数组以0为结尾,所以必须添加一个长度,否则报错
        name = new char[strlen(n) + 1];
        strcpy(name, n);

        cout << "构造函数执行:name=" << name << ",age =" << age << endl;
    }
    Teacher(const Teacher& t) {
        //拷贝构造函数不需要执行这一步(重载=号需要)
        //为什么?这和二者的执行时机有关,拷贝构造函数是在对象未分配内存之前通过=号赋值时执行
        //而重载=号运算符是在对象已经分配内存后,重新赋值时会执行,可以看main方法中的执行
        //虽然两者都使用了=号,但是执行时机不同,这也是为什么拷贝构造函数不需要释放内存的原因
        //因为他执行前并没有分配内存
        //if (name != NULL) {
        //  delete[]name;
        //  name = NULL;
        //  age = 0;
        //}
        age = t.age;
        //注意:char数组以0为结尾,所以必须添加一个长度,否则报错
        name = new char[strlen(t.name) + 1];
        strcpy(name, t.name);

        cout << "拷贝构造函数执行:name=" << name << ",age =" << age << endl;
    }
    ~Teacher() {
        if (name != NULL) {
            delete[]name;
            name = NULL;
            age = 0;
        }
        cout << "析构函数执行" << endl;
    }
    friend ostream& operator<<(ostream& cout, Teacher& t) {
        cout << "<<函数执行teacher name=" << t.name << " teacher age=" << t.age << endl;
        return cout;
    }

    Teacher& operator=(Teacher& t) {
        if (name != NULL)
        {
            delete[]name;
            name = NULL;
            age = 0;
        }
        //注意:char数组以0为结尾,所以必须添加一个长度,否则报错
        name = new char[strlen(t.name) + 1];
        strcpy(name, t.name);
        age = t.age;
        cout << "等号函数执行:name=" << name << ",age =" << age << endl;
        return *this;
    }

    void print() {
        cout << "name = " << name << ",age = " << age << endl;
    }
};

void method() {
    MyVector<Teacher> v(3);
    Teacher t(12, "小明");
    Teacher t2(13, "张三");
    Teacher t3(14, "李四");
    v[0] = t;
    v[1] = t2;
    v[2] = t3;

    Teacher temp = t;
    Teacher temp2 = t2;
    Teacher temp3 = t3;

    for (int i = 0; i < v.m_len; i++) {
        cout << v[i] << " ";
    }

}

void main() {
    method();
    system("pause");
}

打印结果:
构造函数执行:name=小明,age =12
构造函数执行:name=张三,age =13
构造函数执行:name=李四,age =14
等号函数执行:name=小明,age =12
等号函数执行:name=张三,age =13
等号函数执行:name=李四,age =14
拷贝构造函数执行:name=小明,age =12
<<函数执行teacher name=小明 teacher age=12
 <<函数执行teacher name=张三 teacher age=13
 <<函数执行teacher name=李四 teacher age=14
 析构函数执行
析构函数执行
析构函数执行
析构函数执行
析构函数执行
析构函数执行
析构函数执行
请按任意键继续. . .

相关文章

  • 15.C++泛型

    模板函数 模板类 函数模板和友元函数 注意,只在重载左移右移运算符的时候使用友元函数,其他都不要用,友元函数容易导...

  • 泛型 & 注解 & Log4J日志组件

    掌握的知识 : 基本用法、泛型擦除、泛型类/泛型方法/泛型接口、泛型关键字、反射泛型(案例) 泛型 概述 : 泛型...

  • 【泛型】通配符与嵌套

    上一篇 【泛型】泛型的作用与定义 1 泛型分类 泛型可以分成泛型类、泛型方法和泛型接口 1.1 泛型类 一个泛型类...

  • 泛型的使用

    泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法 泛型类 泛型接口 泛型通配符 泛型方法 静态方法与...

  • Java 泛型

    泛型类 例如 泛型接口 例如 泛型通配符 泛型方法 类中的泛型方法 泛型方法与可变参数 静态方法与泛型 泛型上下边...

  • 探秘 Java 中的泛型(Generic)

    本文包括:JDK5之前集合对象使用问题泛型的出现泛型应用泛型典型应用自定义泛型——泛型方法自定义泛型——泛型类泛型...

  • Web笔记-基础加强

    泛型高级应用 自定义泛型方法 自定义泛型类 泛型通配符? 泛型的上下限 泛型的定义者和泛型的使用者 泛型的定义者:...

  • 重走安卓进阶路——泛型

    ps.原来的标题 为什么我们需要泛型? 泛型类、泛型接口和泛型方法(泛型类和泛型接口的定义与泛型方法辨析); 如何...

  • Kotlin泛型的高级特性(六)

    泛型的高级特性1、泛型实化2、泛型协变3、泛型逆变 泛型实化 在Java中(JDK1.5之后),泛型功能是通过泛型...

  • Java 19-5.1泛型

    泛型类定义泛型类可以规定传入对象 泛型类 和泛型方法 泛型接口 如果实现类也无法确定泛型 可以在继承类中确定泛型:

网友评论

      本文标题:15.C++泛型

      本文链接:https://www.haomeiwen.com/subject/jumvsctx.html