C++复习五:泛型编程及C++的类型转换

文章目录

  • 泛型编程
    • 函数模板
      • 函数模板和普通函数的区别
        • 类型转换
        • 调用顺序
        • 函数模板也可以进行函数重载
      • 模板的机制
      • 函数的局限性
    • 类模板
      • 类模板做函数参数
      • 指定传入类型
        • 参数模板化
        • 整体类型化
      • 类模板的继承问题
      • 类外实现模板类的成员函数
      • 类模板的分文件编写
      • 类模板和友元函数
  • C++类型转换
      • 静态转换 static_cast
      • 动态转换(dynamic_casT)
      • 常量转换(const_cast)
      • 重新解释转换(reinterpret_cast)

泛型编程

泛型编程是从一个抽象层面描述一种类型的算法,不管容器类型是什么,是一种不同于OOP的角度来抽象具体算法

C++用模板来实现泛型编程,模板分为函数模板和类模板

函数模板

template<class/typename T> //告诉编译器遇到T不要报错
mySwap(T &a, T &b) //类型参数化,在使用时会进行自动类型推导,将a和b的类型传给T

mySwap<int>(a, b) //显式指定类型

在使用函数模板时,一定要让编译器知道T是什么类型,否则无法使用

函数模板和普通函数的区别

类型转换

普通函数可以进行隐式类型转换(显示表示)
函数模板不可以

调用顺序

当普通函数对函数模板进行了函数重载时,优先调用普通函数,如果普通函数出错,则出错,不会再去调用函数模板
如果需要强制调用函数模板,可以加 <> 来调用

函数模板也可以进行函数重载

如果函数模板可以产生更好的匹配,优先调用函数模板

模板的机制

  • 模板并不是万能的,不能通用所有的数据类型
  • 模板不能直接调用,生成后的模板函数才可以调用
  • 函数模板会进行两次编译,第一次对函数模板进行编译,检查语法错误,第二次对替换T 的类型进行编译

函数的局限性

模板不能解决所有的类型,可以使用第三代具体化来实现

//函数模板
template<class T>
bool myCompare(T &a, T&b)
{
	if(a ==b)
	{
		return true;
	}
	return false;
}

//第三代具体化,如果能直接匹配,就优先调用
template bool myCompare<Person>(Person &a, Person &b)
{
	if(a.age == b.age)
	{
		return true;
	}
	return false;
}

类模板

template <class NameType, class AgeName = int> //可以有默认类型参数
class Person
{
public:
	Person(NameType name, AgeType age)
	{
		m_name = name;
		m_age = age;
	}
	
	NameType m_name;
	AgeType age;
};

Person<string, int> p("heihei",100);  //函数模板可以进行自动类型推导,而类模板不行

类模板的成员函数在未确定时不会创建,只有确认了才会创建


类模板做函数参数

指定传入类型

template<class NameType, class AgeType>
class Person
{
public:
    Person(NameType name, AgeType age)
    {
        this->m_name = name;
        this->m_age = age;
    }

    NameType m_name;
    AgeType m_age;
};

void doWork(Person<string,int> &p)
{
    cout << "Name: " << p.m_name << endl;
    cout << "Age: " << p.m_age << endl;
}

参数模板化

template<class T1, class T2>
void doWork2(Person<T1,T2> &p)
{
    cout << "Name: " << p.m_name << endl;
    cout << "Age: " << p.m_age << endl;
}

整体类型化

template <class T>
void doWork3(T &p)
{
	//查看数据类型
    cout << typeid(T).name() << endl;

    cout << "Name: " << p.m_name << endl;
    cout << "Age: " << p.m_age << endl;
}

类模板的继承问题

template <class T>
class Person
{
public:
    T m_A;
};
//在普通类继承之前,派生类必须知道基类的大小
//在继承时显式声明
class child : public Person<int>
{
public:
    int m_B;
};

//当派生类是模板类时,可以模板化
template <class T1, class T2>
class child1 : public Person<T1>
{
public:
    T2 m_c;
};

类外实现模板类的成员函数

template <class T>
class Person
{
public:
	Person();
	void showPerson();
	
    T m_A;
};

//类外实现成员函数
template <class T>
Person<T>::Person()
{
	cout << "Person::person" << endl;
}

template <class T>
void Person<T>::showPerson()
{
	cout << "Person::showPerson" << endl;
}

类模板的分文件编写

尽量不要份文件编写,写到一个文件即可, .hpp就是类模板的文件

类模板和友元函数

//需要让编译器提前看到友元函数的声明,如下
template <class T>class Person3;
template <class T>void printPerson(Person3<T> &p);

template <class T>
class Person
{
    //友元函数在类外实现,不加<>会将类外的实现当做函数模板,而类内的声明是一个普通函数,优先调用普通函数,而普通函数又没有声明,所以会报错
    //加了<>之后,利用空参数列表,告诉编译器这是一个模板函数的声明
    friend void printPerson<>(Person<T> &p);
    //友元函数在类内实现,加了friend,编译器会自动认为这是一个全局函数
    friend void printPerson(Person<T> &p)
    {
        cout << p.m_A << endl;
    }
public:
    Person(T num);

    T m_A;
};

template <class T>
Person<T>::Person(T num)
{
    this->m_A = num;
}

template <class T>
void printPerson(Person<T> &p)
{
    cout << p.m_A << endl;
}

void test03()
{
    Person3<int> p(100);
    printPerson(p);
}

C++类型转换

类型转换是将一种数据类型转换成另一种数据类型,转换是非常有用的,但是也会带来一些其他的问题,例如越界问题。尽量少的去做类型转换

静态转换 static_cast

  • 父类和子类之间的转换:子类转父类是安全的,父类转子类不安全(基类和子类之间的转换建议用dynamic_cast),不能进行无关类型的转换
  • 基本数据类型的转换:int, char, float, struct, enum等
  • 把空指针转化成目标类型的空指针
  • 把任何类型的表达式转换成void类型
  • static_cast不能去掉类型的const, volitale属性(应该使用const_cast)
//基本数据类型转换
void test01()
{
    char a = 'a';
    double b = static_cast<double>(a);

    cout << "b = " << b << endl;
}

//父类和子类指针的转换
class Base{};
class Child : public Base{};
class Other{};
void test02()
{
    Base* base = NULL;
    Child* child = NULL;

    //父类转子类,不安全
    Child* child2 = static_cast<Child*>(base);
    //子类转父类,安全
    Base* base2 = static_cast<Base*>(child);
    //错误,不能进行无关类型的转换
    //Other* other = static_cast(base);
}

//父类和子类引用的转换
void test03()
{
    Base base;
    Child child;

    Base &base1 = base;
    Child &child1 = child;
    //子转父
    Base &base3 = static_cast<Base&>(child1);
    //父转子
    Child &child3 = static_cast<Child&>(base1);
}

动态转换(dynamic_casT)

有条件转换,动态类型转换,运行时类型安全检查(转换失败返回NULL)

  • 不能进行基本数据类型转换
  • 父与子之间的安全转换(子转父)
  • 发生多态的情况都可以
class Base{
public:
    virtual void print()
    {
        cout << "Base" << endl;
    }
};
class Child : public Base
{
public:
    virtual void print()
    {
        cout << "Child" << endl;
    }
};

void test01()
{
    //不支持基本类型
    //char a = 'a';
    //double b = dynamic_cast(a);

    //父子转换中的安全类型
    Base *base = NULL;
    Child *child = new Child;
    //子转父,安全
    Base *base2 = dynamic_cast<Base*>(child);
    base2->print();
    //父转子,不安全,无法转换,但是使用多态之后就可以啦
    Child *child2 = dynamic_cast<Child*>(base);
    //child2->print();
}

常量转换(const_cast)

  • 去掉类型的 const 或 volatile 属性
  • 不能对非指针或非引用的变量使用
  • 常量指针被转化成非常量指针,并且仍然指向原来的对象
  • 常量引用被转换成非常量引用,并且仍然指向原来的对象
class Base{
public:
    virtual void print()
    {
        cout << "Base" << endl;
    }
};
class Child : public Base
{
public:
    virtual void print()
    {
        cout << "Child" << endl;
    }
};

void test01()
{
    //不支持基本类型
    //char a = 'a';
    //double b = dynamic_cast(a);

    //父子转换中的安全类型
    Base *base = NULL;
    Child *child = new Child;
    //子转父,安全
    Base *base2 = dynamic_cast<Base*>(child);
    base2->print();
    //父转子,不安全,无法转换,但是使用多态之后就可以啦
    Child *child2 = dynamic_cast<Child*>(base);
    //child2->print();
}

重新解释转换(reinterpret_cast)

最不安全的转换机制,仅仅重新解释类型,但没有进行二进制的转换:

  1. 转换的类型必须是一个指针、引用、算术类型、函数指针或者成员指针。
  2. 在比特位级别上进行转换。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,再把该整数转换成原类型的指针,还可以得到原先的指针值)。但不能将非32bit的实例转成指针。
  3. 最普通的用途就是在函数指针类型之间进行转换。
  4. 很难保证移植性

你可能感兴趣的:(C++,c++,类,多态)