C++系列笔记(七)

简介: C++系列笔记(七)

类型转换运算符


  C++提供了一种新的类型转换运算符,专门用于基于继承的情形,这种情形在C语言编程中并不存在。4个C++转换类型如下;

  • static_cast
  • dynamic_cast
  • reinterpret_cast
  • const_cast

这4个类型转换运算符的使用语法相同:destnation_type resulr = cast_type<destination_type> (object_to_be_casted);使用static_cast  使用static_cast可将指针向上转换为基类类型,也可向下转换为派生类型,如下面的示例代码所示:

Base*pBase=newDerived ();   //constract a Derived objectDerived*pDerived=static_cast<Derived*>(pBase);   //ok!

PS : 将 Derived转换为 Base被称为向上转换,无需使用任何显式类型转换运算符就能进行这种转换:

DerivedobjDerived;
Base*pBase=&objDerived;   //ok

将 Base转换为 Derived被称为向下转换,如果不使用显式类型转换运算符,就无法进行这种转换. 除用于向上转换和向下转换外,static_cast还可在很多情况下将隐式类型转换为显式类型,以引起程序员或代码阅读人员的注意:

doubledPi=3.14159265;
intNum=static_cast<int>(dPi);

使用dynamic_cast和运行阶段类型识别


  顾名思义,与静态类型转换相反,动态类型转换在运行阶段(即应用程序运行时)执行类型转换。可检查 dynamic_cast操作的结果,以判断类型转换是否成功。使用 dynamic_cast运算符的典型语法如下:

destination_type*pDest=dynamic_cast<class_type*> (pSource);
if (pDest)    //check for sucess of the casting operation before using pointerpDest->CallFunc();

实例代码如下:

#include<iostream>#include<string>usingnamespacestd;
classFish{
public:
virtualvoidSwim()
        {
cout<<"Fish swims in water"<<endl;
        }
virtual~Fish() {}
};
classTuna:publicFish{
public:
voidSwim()
        {
cout<<"Tuna swims real fast in the sea"<<endl;
     }
voidBecomeDinner()
        {
cout<<"Tuna become dinner in sushi"<<endl;
     }
};
classCarp :publicFish{
public:
voidSwim()
        {
cout<<"Carp swims real slow in the lake"<<endl;
        }
voidTalk()
        {
cout<<"Carp talked crap"<<endl;
        }
};
voidDetectFishType(Fish*InputFish)
{
Tuna*pIsTuna=dynamic_cast<Tuna*>(InputFish);
if (pIsTuna)
        {
cout<<"Detect Tuna. making Tuna Dinner:"<<endl;
pIsTuna->BecomeDinner();
        }
Carp*pIsCarp=dynamic_cast<Carp*>(InputFish);
if (pIsCarp)
        {
cout<<"Detect Carp. making Carp Talk:"<<endl;
pIsCarp->Talk();
        }
cout<<"Verifing type using virtual Fish::Swim:"<<endl;
InputFish->Swim();
}
intmain()
{
CarpmyLunch;
TunamyDinner;
DetectFishType(&myDinner);
cout<<endl;
DetectFishType(&myLunch);
return0;
}

程序输出结果:

DetectTuna. makingTunaDinner:
TunabecomedinnerinsushiVerifingtypeusingvirtualFish::Swim:
TunaswimsrealfastintheseaDetectCarp. makingCarpTalk:
CarptalkedcrapVerifingtypeusingvirtualFish::Swim:
Carpswimsrealslowinthelake

使用reinterpret_cast

reinterpret_cast是C++中与C风格类型转换最接近的类型转换运算符。它让程序员能够将一种对象类型转换为另一种,不管它们是否相关;也就是说,它使用如下所示的语法强制重新解释类型:

Base*pBase=newDerived ();
CUnrelated*pUnrelated=reinterpret_cast<CUnrelated*>(pBase);

这种类型转换实际上是强制编译器接受static_cast通常不允许的类型转换,通常用于低级程序(如驱动程序).注意:使用reinterpret_cast时,程序员将收到类型转换不安全(不可移植)的警告。应尽量避免在应用程序中使用 reinterpret_cast。

使用const_cast

const_cast让程序员能够关闭对象的访问修饰符 const。您可能会问:为何要进行这种转换?在理想情况下,程序员将经常在正确的地方使用关键字const。不幸的是,现实世界并非如此。如下面代码所示

classSomeClass{
public:
//……voidDisplayMembers;   //a display function ought to be const};

然而,DisplayMembers()本应为 const 的,但却没有这样定义。如果 SomeClass 归您所有,且源代码受您控制,则可对DisplayMembers()进行修改。然而,在很多情况下,它可能属于第三方库,无法对其进行修改。在这种情况下,const_cast将是您的救星。

voidDisplayMembers(constSomeClass&mData)
{
mData.DisplayMembers();   //编译错误//reason for failure:call to a non-const member using a const refernence}

所以应该这样修改:

voidDisplayMembers(constSomeClass&mData)
{
SomeClass&refData=const_cast<SomeClass&>(mData)
refData.DisplayMembers():  //Allowed!}

除非万不得已,否则不要使用const_cast来调用非const函数。一般而言,使用const_cast来修改const对象可能导致不可预料的行为。

宏和模板简介


预处理器与编译器


定义常量

#define identifier value

使用宏避免多次包含

在预处理器看来,两个头文件彼此包含对方会导致递归问题。为避免这种问题,可结合使用宏以及预处理器编译指令#ifndef和#endif。包含<header2.h>的head1.h类似于下面这样:

#ifndef HEADER1_H_#define HEADER1_H_#include<header2.h>classClass1{
//code};
#endif   //end of header1.h

header2.h与1差不多,替换一下就可以了。预处理器首次处理header1.h并遇到#ifndef后,发现宏HEADER1_H_还未定义,因此继续处理。#ifndef后面的第一行定义了宏HEADER1_H_,确保预处理器再次处理该文件时,将在遇到包含#ifndef的第一行时结束,因为其中的条件为false。

使用define定义宏函数


典型代码:#define SQUARE(x) ((x)*(x))

宏函数经常进行简单的计算,有助于改善代码的性能。

使用assert()宏验证表达式


assert宏让您能够插入检查语句,对表达式或变量的值进行验证。要使用assert宏,需要包含<assert.h>,其语法如下:

#include<assert.h>intmain()
{
char*SayHello=newchar [25];
assert(SayHello!=NULL);
//other code;delete[] SayHello;
return0;
}

上述代码在指针无效时可以指出来。

相关文章
|
5月前
|
算法 C++
算法笔记:递归(c++实现)
算法笔记:递归(c++实现)
|
5月前
|
编译器 C++
《Effective C++ 改善程序与设计的55个具体做法》 第一章 笔记
《Effective C++ 改善程序与设计的55个具体做法》 第一章 笔记
|
3月前
|
C++ 容器
【C/C++笔记】迭代器
【C/C++笔记】迭代器
22 1
|
3月前
|
存储 安全 程序员
【C/C++笔记】迭代器范围
【C/C++笔记】迭代器范围
63 0
|
4月前
|
C++ Windows
FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg
在Windows上使用Visual Studio 2022进行FFmpeg和SDL2集成开发,首先安装FFmpeg至E:\msys64\usr\local\ffmpeg,然后新建C++控制台项目。在项目属性中,添加FFmpeg和SDL2的头文件及库文件目录。接着配置链接器的附加依赖项,包括多个FFmpeg及SDL2的lib文件。在代码中引入FFmpeg的`av_log`函数输出"Hello World",编译并运行,若看到"Hello World",即表示集成成功。详细步骤可参考《FFmpeg开发实战:从零基础到短视频上线》。
113 0
FFmpeg开发笔记(三十九)给Visual Studio的C++工程集成FFmpeg
|
6月前
|
存储 C++ 容器
黑马c++ STL部分 笔记(7) list容器
黑马c++ STL部分 笔记(7) list容器
|
6月前
|
算法 C++ 容器
黑马c++ STL常用算法 笔记(5) 常用算术生成算法
黑马c++ STL常用算法 笔记(5) 常用算术生成算法
|
6月前
|
算法 C++ 容器
黑马c++ STL常用算法 笔记(4) 常用拷贝和替换算法
黑马c++ STL常用算法 笔记(4) 常用拷贝和替换算法
|
6月前
|
存储 算法 搜索推荐
黑马c++ STL常用算法 笔记(3) 排序算法
黑马c++ STL常用算法 笔记(3) 排序算法
|
6月前
|
算法 C++
黑马c++ STL常用算法 笔记(2) 查找算法
黑马c++ STL常用算法 笔记(2) 查找算法