初学者的总结:函数指针、强制类型转换

1、指针 * 号的位置总结

int *a,b;                               //代表指针a,整数b
int* a,b;                               //同上
int * a,b;                              //同上

2、顶层/底层const

指针本身是一个对象,它又可以指向另外一个对象。所以有两个定义:

  1. 顶层const:指针本身是常量
  2. 底层const:指针所指的对象是常量

执行对象copy的时候,顶层const不受影响,底层const的限制不能忽视,他们必须同时拥有同样的底层const资格或者两个对象的数据类型必须能够转换才行,一般来说非常量可以转换为常量,反之则不行。

const int &r = ci;                      //底层const,用于声明引用的都是底层const
const int* p = &value;                  //底层const,p指向的内容不变,
//此时可以   p = &other_value
//不可以     *p=123;

int * const q = &value;                 //顶层const,q指向的地址不变,地址里面的内容可以变
const int tmp = 42;                     //顶层const
//不可以     p = &other_value
//可以       *p=123;

const详解

3、函数指针

c语言函数指针定义形式:返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,…);

c++函数指针的定义形式:返回类型 (类名称::*函数成员名称)(参数类型,参数类型,参数类型,….);

函数类型由它的返回值和参数类型决定,与函数名无关

bool length_compare(const string &, const string &);

上述函数类型是:bool (const string &, const string &);
上述函数指针pf:bool (*pf)(const string &, const string &);

3.1 最简单的例子

#include <iostream>
using namespace std;

class test {
public:
    int fun(int a, char c){
        cout<<"this is fun call:"<<a<<" "<<c<<endl;
        return a;
    }

    int fun_const(int a, char c[]) const {
        cout<<"this is fun_const  call:"<<a<<" "<<c<<endl;
        return a;
    }

    static int fun_static(char buf[]){
        cout<<"this is fun_static call:"<<buf<<endl;
        return 11;
    }
};

int hello_world(char str[]){
    cout << "hello world " << str << endl;
}

int main()
{
    ///pHello = hello_world; 和 pHello = &hello_world; 等价,类成员函数同理

    /// hello world
    int (*pHello)(char str[]) = nullptr;
    pHello = hello_world;
    pHello("test1");
    (*pHello)("test2");
    pHello = &hello_world;
    pHello("test3");
    (*pHello)("test4");

    ///普通成员函数
    int (test::*pfun)(int, char) = nullptr;         //一定要加类名
    pfun = &test::fun;                              //加取地址符号
    test mytest;
    (mytest.*pfun)(1, 'a');                         //调用是一定要加类的对象名和*符号

    ///const 类成员函数(基本跟普通成员函数相同)
    int (test::*pconst)(int, char [])const = nullptr;  //一定要加const
    pconst = &test::fun_const;
    (mytest.*pconst)(2,"static");

    /// 类的静态成员函数指针和c的指针的用法相同
    int (*pstatic)(char buf[]) = nullptr;           //不需要加类名
    pstatic = test::fun_static;                     //不加取地址符号
    pstatic("static_1");
    pstatic = &test::fun_static;                    //取地址符
    (*pstatic)("static_&");

    return 0;
}

3.2 函数指针作为函数返回数据:

//如果是非类成员函数,在main函数中把对应的类去掉就行了
#include <iostream>
using namespace std;

class test
{
public:
    int fun(int a, char c)
    {
        cout<<"this is fun call:"<<a<<" "<<c<<endl;
        return a;
    }
};

class test2
{
    public:
    //成员函数fun1,参数是double,
    //返回值:test的成员函数指针 int(test::*)(int, char)
    int (test::*fun1(double d))(int, char)
    {
        cout<<d<<endl;
        return &test::fun;
    }
};

int main()
{
    test mytest;
    test2 mytest2;
    int (test::*p)(int, char) = mytest2.fun1(3.33);
    (mytest.*p)(1, 'a');
    return 0;
}

3.3 typedef、using简化函数指针,以及函数指针数组:

#include <stdio.h>
#include <stdlib.h>

float add(float a,float b)
{
    printf("%f\n",a+b);
    return a+b;
}
float minu(float a,float b)
{
    printf("%f\n",a-b);
    return a-b;
}

//用pfunType 来表示float(*)(float, float)
typedef float(*pfunType)(float, float);

int main()
{
    pfunType p = &add;//定义函数指针变量
    p(3.33, 2.22);
    pfunType parry[2] = {&add, &minu};//定义函数指针数组
    parry[1](3.33, 2.22);

    using use_function = float(*)(float, float);
    use_function temp = &add;
    temp(123.11,555.1);

    //函数指针作为参数可以定义为:void fun(pfunType p)
    //函数指针作为返回值可以定义为:pfunType fun();

    ///原始函数指针数组为:float (*pfunArry[2])(float, float) = {&add, &minu};
    //double k = pfunArry[0](3.33,2.22);// 调用方式
    return 0;
}

4、强制类型转换

避免使用强制类型转换,因为它干扰了编译器的正常的类型检查,reinterpret_cast尤为明显。static_cast、dynamic_cast也不应该频繁的使用,const_cast在重载函数的上下文中使用无可厚非。否则将错误百出。

4.1 reinterpret_cast

reinterpret_cast作用:

  1. 网络理解:从一个指向函数、对象、类成员函数、类数据成员等等的指针到另外一个不同类型的指针
  2. C++11primer:运算对象的位模式提供较低层次上的重新解释

IBM的C++指南、C++之父Bjarne Stroustrup的FAQ网页和MSDN的Visual C++也都指出:错误的使用reinterpret_cast很容易导致程序的不安全,只有将转换后的类型值转换回到其原始类型,这样才是正确使用reinterpret_cast方式。

int *ip;
char *pc = reinterpret_cast<char *>(ip);

我们必须牢记pc的真实对象是一个int类型,否则可能出现运行时的错误,例如:
string str(pc);

4.2 static_cast、void*

特许规则:

虽然不能隐式地将一个左值转换为右值引用,但是可以使用static_cast显示地将一个左值转换为一个右值引用。但是还是建议使用std::move(),使得去查找截断左值的代码变得容易。

一般性规则:

任何具有明确定义的类型转换,只要不包含底层的const,都可以使用static_cast

//i、j都是整形
double t = static<double>(j) / i;       //显示告诉编译器,我们不在乎精度的损失,减少警告

//我们必须确保转换后的类型就是指针所指的类型,否则出现未定义的错误
void *p = &d;                           //任何非常量对象地址都能存入void*
double *tp = static<double*>(p);

void *

任何非常量对象地址都能存入void*,以void*的视角来看内存空间,无法访问内存空间的内容,因为我们不知道是什么类型

4.3 dynamic_cast

使用形式:

其中type为一个类类型,并且通常情况下应该含有一个虚函数。
dynamic_cast<type*>(e);                 //e必须为一个有效指针
dynamic_cast<type&>(e);                 //e必须为一个左值
dynamic_cast<type&&>(e);                //e必须不是一个左值

在上面所有形式中,需要符合下面三个条件之一就能够转换成功:

  1. e的类型是目标type的公有派生类
  2. e的类型是目标type的公有基类
  3. e的类型是目标type的类型
  4. 可以对一个空指针进行转换,得到的结果也是空指针

否则将会转换失败,得到的结果将是0,并且抛出一个bad_cast的异常

4.4 const_cast

const_cast只能改变运算对象的底层const,函数指针和成员函数指针不可用于 const_cast

const A *pca1 = new A;                          //A是一个类
A *pa2 = const_cast<A*>(pca1);                  //常量对象转换为非常量对象 

const A &a1 = a0;
A a2 = const_cast<A&>(a1);                     //常量引用转为非常量引用 

//下面两情况是未定义的情况
const char *pc;
char *p = const_cast<char *>(pc);               //正确,但是通过p写值是未定义的行为,因为pc没有值


const int j = 3; // 声明 j 为 const
int* pj = const_cast<int*>(&j);
// *pj = 4;      // 未定义行为

4.5 dynamic_pointer_cast、lexical_cast、duration_cast

以后有空再学了,目前使用不到

  1. dynamic_pointer_cast: Dynamic cast of shared_ptr. Returns a copy of sp of the proper type with its stored pointer casted dynamically from U* to T*.
  2. boost::lexical_cast: 为数值之间的转换(conversion)提供了一揽子方案,比如:将一个字符串"123"转换成整数123,代码如下:
  3. duration_cast()函数提供了在不同的时间单位之间进行转换的功能。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章