C++【11】(类型转换和函数模板)

953 阅读5分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

一、C++类型转换

C++几乎完全兼容C语言

C语言中的类型转换有两种:

  1. 隐式转换,自动转换
  2. 显式转换,强制类型转换

C++里面显式转换处理强制类型转换以外,还有四种特定场合的转换方式,新类型的强制转换可以提供更好的控制强制转换过程,允许控制各种不同种类的强制转换。

C++风格的强制转换其他的好处是,它们==能更清晰的表明它们要干什么==

C++显式转换的方式:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
int a = 100; char *p = &a; //安全的
char b = 100; int *q = &b; //不安全的
Parent p1; Son *s1 = &p1; //不安全的
下行转换:子类的指针保存父类对象的地址

Son s2; Parent *p2 = &s2; //安全的
上行转换:父类的指针保存子类对象的地址
#include <iostream>

using namespace std;

//C语言中的类型转换
//隐式转换
void test1()
{
    float a = 100.345;
    float b = 200.9342;
    int c = a + b;
    cout << "c = " << c << endl;

    //无符号的数据和有符号的数据相运算,
    //会先将有符号的转化为无符号的然后再运算
    int m = -20;
    unsigned int n = 6;
    cout << m + n << endl;
    printf("m + n = %d\n", m + n);
    printf("m + n = %u\n", m + n);

    if(m + n > 0)
    {
        cout << "m+n>0" << endl;
    }
    else
    {
        cout << "m+n<=0" << endl;
    }
}
//显式转换:强制类型转换
void test2()
{
    int a = 10;
    int b = 4;
    float c = (float)a / (float)b;

    cout << "c = " << c << endl;
}

//C++支持C语言中的类型转换,还有四个特定场合适用的显式类型转换
class Parent{
};

class Son:public Parent{
};

//static_cast:静态转换
//用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
//  进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
//  进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
//用于基本数据类型之间的转换,如把int转换成char,把char转换成int。
//  这种转换的安全性也要开发人员来保证
void test3()
{
    //static_cast可以用于基本数据类型之间转换
    int a = 0x12345678;
    char b = static_cast<char>(a);
    printf("b = %#x\n", b);

    //static_cast不允许用于基本数据类型的指针类型之间的转换
    //char *p = static_cast<char *>(&a);

    Son s1;
    Parent *p1 = static_cast<Parent *>(&s1);

    //static_cast不会检测下行转换的安全性
    Parent p2;
    Son *s2 = static_cast<Son *>(&p2);
}

//dynamic_cast:动态类型转换
//主要用于类层次间的上行转换和下行转换;
//在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
//在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全;
void test4()
{
    //dynamic_cast不能用于基本数据类型以及指针之间相互转换
//    int a = 0x12345678;
//    char b = dynamic_cast<char>(a);
//    printf("b = %#x\n", b);
//    char *p = dynamic_cast<char *>(&a);

    //dynamic_cast只能用于上行转换,意味着只有安全的才可以
    //下行转换不安全,所以如果使用dynamic_cast就会报错
    Son s1;
    Parent *p1 = dynamic_cast<Parent *>(&s1);

//    Parent p2;
//    Son *s2 = dynamic_cast<Son *>(&p2);
}

//const_cast:常量转换
//该运算符用来修改类型的const属性。。
//		常量指针被转化成非常量指针,并且仍然指向原来的对象;
//		常量引用被转换成非常量引用,并且仍然指向原来的对象;
//注意:不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const.
void test5()
{
    //不能直接对非指针和非引用的变量使用const_cast操作符去直接移除它的const
//    const int a = 100;
//    int b = const_cast<int>(a);

    const int a = 100;
    int *p = const_cast<int *>(&a);

    *p = 888;
    cout << a << endl;
    cout << *p << endl;
}

//reinterpret_cast:重新解释转换
//类似于强制类型转换
//这是最不安全的一种转换机制,最有可能出问题。
//主要用于将一种数据类型从一种类型转换为另一种类型。
//它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针

int main()
{
    test5();

    return 0;
}

二、函数模板和类模板

2.1 函数模板

#include <iostream>

using namespace std;

//C语言里面一个程序不能有同名的函数
#if 0
void myfun1(int m, int n)
{
    cout << "m + n = " << m + n << endl;
}

void myfun2(float m, float n)
{
    cout << "m + n = " << m + n << endl;
}
#endif

//C++函数重载主要保证参数的个数或者类型不一样就可以用相同的函数名
#if 0
void myfun(int m, int n)
{
    cout << "m + n = " << m + n << endl;
}

void myfun(float m, float n)
{
    cout << "m + n = " << m + n << endl;
}
#endif

//函数模板主要就是用于解决通过一个函数可以操作任意类型的数据
//只需要在编写函数的时候将特定的类型通过template声明之后就可以直接使用
//函数模板也可以重载
template<typename myType>
void myfun(myType m, myType n)
{
    cout << "m + n = " << m + n << endl;
}

#if 0
template<typename T1, typename T2>
void myfun(T1 m, T2 n)
{
    cout << "m + n = " << m + n << endl;
}
#endif

void test1()
{
    myfun(100, 20);
    myfun(4.13, 8.19);
    myfun('a', 'b');
}

//函数模板的调用方式
void test2()
{
    //方法1:隐式调用
    myfun(100, 20);
    myfun(4.13, 8.19);
    myfun('a', 'b');
    //如果两个参数的类型一致,必须传一致类型的数据,否则报错
    //myfun(100, 3.14);

    //方法2:显式调用且不指定类型
    myfun<>(100, 20);
    myfun<>(4.13, 8.19);
    myfun<>('a', 'b');
    //如果两个参数的类型一致,必须传一致类型的数据,否则报错
    //myfun<>(100, 3.14);

    //方法3:显式调用并指定数据类型(推荐)
    myfun<int>(100, 20);
    myfun<double>(4.13, 8.19);
    myfun<char>('a', 'b');
    //此种方式如果类型不一致,会将不一致类型的数据进行隐式转换
    myfun<int>(100, 3.14);
}

//当函数模板重载一个普通函数后的优先调用问题
//只有当隐式调用时,且传输参数的类型与普通函数的参数类型
//一致时会调用普通函数,否则都调用函数模板
//声明类型的时候既可以用typename也可以使用class
void myfun(int m, int n)
{
    cout << "普通函数" << endl;
    cout << "m + n = " << m + n << endl;
}

void test3()
{
    //函数模板隐式调用
    myfun(100, 200);
    myfun(100.5, 200.6);

    //显式调用并不指定类型
    myfun<>(999, 1);
    //显式调用并指定类型
    myfun<int>(999, 1);
}

int main()
{
    test3();
    return 0;
}

练习:使用函数模板实现简单选择法排序

#include <iostream>

using namespace std;

//使用函数模板实现简单选择法排序

template<typename Type>
void EasySort(Type *arr, int len)
{
    int i = 0, j;
    Type value;
    for(i = 0; i < len - 1; i++)
    {
        //先用第一个元素与后面的每一个进行对比,如果有小的就交换
        for(j = i + 1; j < len; j++)
        {
            if(arr[i] > arr[j])
            {
                value = arr[i];
                arr[i] = arr[j];
                arr[j] = value;
            }
        }
    }
}

int main()
{
    int a[] = {10, 87, 23, 67, 9, 100, 77, 88, 34};
    cout << "排序前:";
    for(int i = 0; i < sizeof(a) / sizeof(int); i++)
    {
        cout << a[i] << " ";
    }
    cout << endl;

    EasySort<int>(a, sizeof(a) / sizeof(int));

    cout << "排序后:";
    for(int i = 0; i < sizeof(a) / sizeof(int); i++)
    {
        cout << a[i] << " ";
    }
    cout << endl;

    return 0;
}

1.2 类模板

1.2.1 类模板的基本使用

#include <iostream>

using namespace std;

//类模板与函数模板差不多都是将数据的类型不指定,
//意味着类中的成员变量的类型可以声明

template<class T1, class T2, class T3>
class Person{
public:
    Person(){}
    Person(T1 a, T2 b, T3 c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
    }

    void printMsg()
    {
        cout << this->a << ", " << this->b << ", " << this->c << endl;
    }
private:
    T1 a;
    T2 b;
    T3 c;
};

void test1()
{
    //类模板实例化对象时必须显式调用并指定数据类型
    Person<int, string, int> p1(1001, "张三", 90);
    p1.printMsg();
}

int main()
{
    test1();
    return 0;
}

4.2.2 类模板中成员函数类内声明类外实现

#include <iostream>

using namespace std;

//类模板与函数模板差不多都是将数据的类型不指定,
//意味着类中的成员变量的类型可以声明

template<class T1, class T2, class T3>
class Person{
public:
    Person(){}
    Person(T1 a, T2 b, T3 c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
    }

    //类模板中的成员函数类内声明类外实现
    void printMsg();

    //类模板中可以再定义函数模板
#if 0
    template<class Type1, class Type2, class Type3>
    void setMsg(Type1 a, Type2 b, Type3 c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
    }
#endif
    template<class Type1, class Type2, class Type3>
    void setMsg(Type1 a, Type2 b, Type3 c);
private:
    T1 a;
    T2 b;
    T3 c;
};

void test1()
{
    //类模板实例化对象时必须显式调用并指定数据类型
    Person<int, string, int> p1(1001, "张三", 90);
    p1.printMsg();
}

template<class T1, class T2, class T3>
void Person<T1, T2, T3>::printMsg()
{
    cout << this->a << ", " << this->b << ", " << this->c << endl;
}

template<class T1, class T2, class T3>
template<class Type1, class Type2, class Type3>
void Person<T1, T2, T3>::setMsg(Type1 a, Type2 b, Type3 c)
{
    this->a = a;
    this->b = b;
    this->c = c;
}

void test2()
{
    Person<int, string, int> p1(1001, "张三", 90);
    p1.printMsg();

    p1.setMsg<int, string, int>(1002, "李四", 88);
    p1.printMsg();
}

int main()
{
    test2();
    return 0;
}

4.2.3 类模板派生出的子类的定义和实例化对象

#include <iostream>

using namespace std;

//类模板与函数模板差不多都是将数据的类型不指定,
//意味着类中的成员变量的类型可以声明

template<class T1, class T2, class T3>
class Person{
public:
    Person(){}
    Person(T1 a, T2 b, T3 c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
    }

    //类模板中的成员函数类内声明类外实现
    void printMsg();

    //类模板中可以再定义函数模板
#if 0
    template<class Type1, class Type2, class Type3>
    void setMsg(Type1 a, Type2 b, Type3 c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
    }
#endif
    template<class Type1, class Type2, class Type3>
    void setMsg(Type1 a, Type2 b, Type3 c);
private:
    T1 a;
    T2 b;
    T3 c;
};

void test1()
{
    //类模板实例化对象时必须显式调用并指定数据类型
    Person<int, string, int> p1(1001, "张三", 90);
    p1.printMsg();
}

template<class T1, class T2, class T3>
void Person<T1, T2, T3>::printMsg()
{
    cout << this->a << ", " << this->b << ", " << this->c << endl;
}

template<class T1, class T2, class T3>
template<class Type1, class Type2, class Type3>
void Person<T1, T2, T3>::setMsg(Type1 a, Type2 b, Type3 c)
{
    this->a = a;
    this->b = b;
    this->c = c;
}

void test2()
{
    Person<int, string, int> p1(1001, "张三", 90);
    p1.printMsg();

    p1.setMsg<int, string, int>(1002, "李四", 88);
    p1.printMsg();
}

class Son1:public Person<int, string, int>{
};


template<class T1, class T2, class T3>
class Son2:public Person<T1, T2, T3>{

};

template<class T1, class T2, class T3, class T4, class T5, class T6>
class Son3:public Person<T1, T2, T3>{
public:
    void setSon3Msg(T4 aa, T5 bb, T6 cc)
    {
        this->aa = aa;
        this->bb = bb;
        this->cc = cc;
    }
    void printSon3Msg()
    {
        cout << this->aa << ", " << this->bb << ", " << this->cc << endl;
    }
    T4 aa;
    T5 bb;
    T6 cc;
};

void test3()
{
    Son1 s1;
    s1.setMsg(1001, "zhangsan", 90);
    s1.printMsg();

    Son2<int, string, int> s2;
    s2.setMsg(1001, "zhangsan", 90);
    s2.printMsg();

    cout << "*************" << endl;

    Son3<int, string, int, char, char, char> s3;
    s3.setMsg<int, string, int>(1001, "王五", 68);
    s3.setSon3Msg('a', 'b', 'c');

    s3.printMsg();
    s3.printSon3Msg();
}

int main()
{
    test3();

    return 0;
}