初學者的總結:函數指針、強制類型轉換

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()函數提供了在不同的時間單位之間進行轉換的功能。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章