C++语法
LDK Lv4

[toc]

强制类型转换

static_cast

用于基本数据类型之间的转换,以及在类层次结构中基类和派生类之间的指针或引用的转换。它在编译时执行,不提供运行时类型检查

如果对象所属的类重载了强制类型转换运算符 T(如 T 是 int、int* 或其他类型名),则 static_cast 也能用来进行对象到 T 类型的转换。

static_cast 不能用于在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,当然也不能用于不同类型的引用之间的转换。因为这些属于风险比较高的转换。

例:

1
2
3
int a = 10;
int b = 3;
double result = static_cast<double>(a) / static_cast<double>(b);

dynamic_cast

用于类层次结构中的指针和引用的转换,它在运行时检查类型的安全性。它要求转换的类型必须包含至少一个虚函数

如果转换失败,dynamic_cast会返回nullptr

例:

1
2
3
4
class Base {};
class Derived : public Base {};
Base* basePtr = new Derived();
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 安全的转换

dynamic_castreinterpret_cast的区别:

  • 用 reinterpret_cast 可以将多态基类(包含虚函数的基类)的指针强制转换为派生类的指针,但是这种转换不检查安全性,即不检查转换后的指针是否确实指向一个派生类对象。
  • dynamic_cast专门用于将多态基类的指针或引用强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回 nullptr 指针。

const_cast

仅用于去除变量的const属性。它主要用于操作指向常量对象的指针或引用的const属性。

例:

1
2
3
4
const int a = 10;
const int* p = &a;
int* q = const_cast<int*>(p);
*q = 20; // 现在*q可以被修改

reinterpret_cast

用于低级的强制类型转换,如改变指针类型或将指针转换为足够长度的整型。它仅仅复制位模式,不改变值本身

[!warning]

这种转换是不安全的,因为它不保证转换结果的有效性:

例如,执意把一个 int* 指针、函数指针或其他类型的指针转换成 string* 类型的指针也是可以的,至于以后用转换后的指针调用 string 类的成员函数引发错误,则需要程序员自行承担查找错误的烦琐工作

例:

1
2
int* a = new int;
double* d = reinterpret_cast<double*>(a);

函数指针

函数指针的定义:

1
2
// 注意 func_pointer只是一个指向特定类型函数的指针,并不是一个函数。要指向哪个函数就将哪个函数赋值给它
return_type (*func_pointer)( data_types arg1, data_types arg2, ...,data_types argn);

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
int test(int a)
{
return a;
}
int main(int argc, const char * argv[])
{
int (*fp)(int a); // 定义一个函数指针,指针名为fp,指向“接受一个int类型参数并且返回值为int类型的函数”。
// int (*fp)(int) = test; // 在定义时初始化
fp = test; // 令fp指向test
// fp = &test; // 效果与上一行一样
cout<<fp(2)<<endl;
return 0;
}

typedef简化函数指针的定义

参见:简化函数指针的定义

指向类成员函数的函数指针

形如:int (A::*p)(void)的函数指针只能指向class A的非静态成员函数,并且调用时必须通过对象提供this指针。原因:含有A::,指向一个需要特定对象才能调用的函数。

形如:int (*p)(void)的函数指针,如果要指向类内的函数,则只能指向类内的static函数,并且可以直接通过p()调用。原因:看形式就知道是一个普通函数指针,而普通函数指针不需要特定对象就可以调用。

指向**非静态(non static)**成员函数的函数指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
class A {
public:
A(int aa = 0) : a(aa) {}

~A() {}

void setA(int aa = 1) {
a = aa;
}

virtual void print() {
cout << "A: " << a << endl;
}

virtual void printa() {
cout << "A1: " << a << endl;
}

private:
int a;
};

class B : public A {
public:
B() : A(), b(0) {}

B(int aa, int bb) : A(aa), b(bb) {}

~B() {}

virtual void print() override {
A::print();
cout << "B: " << b << endl;
}

virtual void printa() override {
A::printa();
cout << "B: " << b << endl;
}

private:
int b;
};

int main() {
A a;
B b;
void (A::*ptr)(int) = &A::setA;
A *pa = &a;

// 对于非虚函数,返回其在内存的真实地址
printf("A::set(): %p\n", &A::setA);
// 对于虚函数, 返回其在虚函数表的偏移位置
printf("A::print(): %p\n", &A::print);
printf("A::printa(): %p\n", &A::printa);

a.print();

a.setA(10);
a.print();

a.setA(100);
a.print();

// 对于指向类成员函数的函数指针,引用时必须传入一个类对象的this指针,所以必须由类实体调用
(pa->*ptr)(1000);
a.print();

(a.*ptr)(10000);
a.print();

return 0;
}
指向**静态(static)**成员函数的函数指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
using namespace std;

class A{
public:

//p1是一个指向非static成员函数的函数指针
void (A::*p1)(void);

//p2是一个指向static成员函数的函数指针
void (*p2)(void);

A(){
/* 对指向非static成员函数的指针
* 和
* 指向static成员函数的指针
* 的变量的赋值方式是一样的,都是&ClassName::memberVariable形式
* 区别在于:
* 对p1只能用非static成员函数赋值
* 对p2只能用static成员函数赋值
*
* 再有,赋值时如果直接&memberVariable,则在VS中报"编译器错误 C2276"
* 参见:http://msdn.microsoft.com/zh-cn/library/850cstw1.aspx
*/
p1 =&A::funa; //函数指针赋值一定要使用 &
p2 =&A::funb;

//p1 =&A::funb;//error
//p2 =&A::funa;//error

//p1=&funa;//error,编译器错误 C2276
//p2=&funb;//error,编译器错误 C2276
}

void funa(void){
puts("A");
}

static void funb(void){
puts("B");
}
};

int main()
{
A a;
//p是指向A中非static成员函数的函数指针
void (A::*p)(void);

(a.*a.p1)(); //打印 A

//使用.*(实例对象)或者->*(实例对象指针)调用类成员函数指针所指向的函数
p = a.p1;
(a.*p)(); // 需要对象提供this指针

A *b = &a;
(b->*p)(); // 需要对象提供this指针

/*尽管a.p2本身是个非static变量,但是a.p2是指向static函数的函数指针,
**所以下面这就话是错的!
*/
// p = a.p2;//error

void (*pp)(void);
pp = &A::funb;
pp(); // 直接调用函数,不需要对象提供this指针

return 0;
}

typedef的四种用法

基本数据类型的别名

1
2
typedef int INTEGER;	// 为int类型定义别名INTEGER
INTEGER num; // num是int类型

自定义数据类型的别名

1
2
3
4
5
typedef class test{	// 这里将class换成struct效果一样
int a=0;
}TEST; // TEST是test的别名

TEST myTest; // myTest是test类型的变量

给数组类型取别名

假设这么一种情况:我需要定义 10 个长度为 100 的int类型数组。最简单的是下面这种方法:

1
2
3
4
5
int arr_1[100];			int arr_2[100];
int arr_3[100]; int arr_4[100];
int arr_5[100]; int arr_6[100];
int arr_7[100]; int arr_8[100];
int arr_9[100]; int arr_10[100];

可见需要写10个数组的定义。

除了上面的方法,还可以用另一种方法快速定义10个数组。

1
2
3
4
typedef int ARR_LENGTH_100[100];

// arr_1 到 arr_10 都是int类型的长度为100的数组
ARR_LENGTH_100 arr_1, arr_2, arr_3, arr_4, arr_5, arr_6, arr_7, arr_8, arr_9, arr_10;

虽然也是写了10个数组的定义,不过要比上一个简洁的多。

简化函数指针的定义

需要先了解:[函数指针](# 函数指针)

假设我们需要定义2个函数指针,这两个函数指针指向的函数类型相同。正常来说是写两句:

1
2
int (*fp1)(int, char);
int (*fp2)(int, char);

那么假设需要定义5个呢?10个呢?总不能复制粘贴10行吧。

这时候就可以用typedef了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int test(int a, char ch) {
/* ... */
return 0;
}

int main() {
typedef int (*fp)(int, char);

fp fp1, fp2;
fp1 = test;
fp2 = test;

return 0;
}

这样就可以一次性定义多个函数指针了。当然得是同类型的才行

这还没完,我们甚至可以定义一个函数指针数组。数组里面每个元素都是指向一个函数的函数指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
void t1(){cout<<"test1"<<endl;}
void t2(){cout<<"test2"<<endl;}
void t3(){cout<<"test3"<<endl;}

int main(int argc, const char * argv[])
{

typedef void (*fp)(void);
fp fp_arr[] = {t1,t2,t3}; // b[] 为一个指向函数的指针数组
fp_arr[0](); // 利用指向函数的指针数组进行下标操作就可以进行函数的间接调用了

return 0;
}

这样也可以一次性定义多个函数指针,并且更加便捷。

typedef#define的区别

本质:typedef是定义别名,而#define是简单的字符串替换

  1. 可以使用其他类型说明符对宏类型名(由#define定义)进行扩展,但对 typedef 所定义的类型名却不能这样做。

    1
    2
    3
    4
    5
    #define INTERGE int
    unsigned INTERGE n; //没问题

    typedef int INTERGE;
    unsigned INTERGE n; //错误,不能在 INTERGE 前面添加 unsigned
  2. 在连续定义几个变量的时候,typedef 能够保证定义的所有变量均为同一类型,而 #define 则无法保证。

    #define

    1
    2
    3
    4
    5
    #define PTR_INT int *
    PTR_INT p1, p2;

    // 经过宏替换后,p1, p2变为:
    int *p1, p2; // p1是int类型的指针变量。p2是int类型的整数变量

    typedef

    1
    2
    typedef int * PTR_INT
    PTR_INT p1, p2;

由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 34.6k 访客数 访问量