文章目錄
1、指針 * 號的位置總結
int *a,b; //代表指針a,整數b
int* a,b; //同上
int * a,b; //同上
2、頂層/底層const
指針本身是一個對象,它又可以指向另外一個對象。所以有兩個定義:
- 頂層const:指針本身是常量
- 底層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;
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作用:
- 網絡理解:從一個指向函數、對象、類成員函數、類數據成員等等的指針到另外一個不同類型的指針
- 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必須不是一個左值
在上面所有形式中,需要符合下面三個條件之一就能夠轉換成功:
- e的類型是目標type的公有派生類
- e的類型是目標type的公有基類
- e的類型是目標type的類型
- 可以對一個空指針進行轉換,得到的結果也是空指針
否則將會轉換失敗,得到的結果將是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
以後有空再學了,目前使用不到
- 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*.
- boost::lexical_cast: 爲數值之間的轉換(conversion)提供了一攬子方案,比如:將一個字符串"123"轉換成整數123,代碼如下:
- duration_cast()函數提供了在不同的時間單位之間進行轉換的功能。