Day5: 5道C++ 面向对象高频题整理

devtools/2024/9/23 14:35:26/

1、强制类型转换运算符

在C++中,强制类型转换运算符(也称为类型转换操作符)用于在不同类型之间进行显式的类型转换。C++提供了四种类型的强制类型转换运算符,分别是static_cast、dynamic_cast、reinterpret_cast和const_cast。

这些强制类型转换运算符的用法如下:

  • static_cast:用于进行静态类型转换,可以将一种类型的值转换为另一种类型,前提是转换是合法的。例如,可以将一个整数转换为浮点数、将指针或引用进行类型转换等。静态类型转换在编译时进行检查,因此需要开发人员保证转换的安全性。
    int x = 10;
    double y = static_cast<double>(x); // 将整数x转换为浮点数Base* basePtr = new Derived(); // Derived是Base的派生类
    Derived* derivedPtr = static_cast<Derived*>(basePtr); // 将基类指针转换为派生类指针
    

  • dynamic_cast:用于进行动态类型转换,主要用于处理多态情况下的类型转换。它可以在运行时检查对象的实际类型,并进行安全的向下转型(派生类到基类)或跨继承层次的类型转换。如果转换是不安全的,dynamic_cast将返回空指针(对于指针转换)或抛出std::bad_cast异常(对于引用转换)。
Base* basePtr = new Derived(); // Derived是Base的派生类
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 安全地将基类指针转换为派生类指针Base* basePtr2 = new Base();
Derived* derivedPtr2 = dynamic_cast<Derived*>(basePtr2); // 转换失败,derivedPtr2将为nullptr
  • reinterpret_cast:用于进行低级别的类型转换,可以将任何指针或引用转换为其他类型的指针或引用,甚至没有关联的类型。reinterpret_cast通常用于处理底层的二进制数据表示,但它的使用需要非常谨慎,因为它会绕过类型系统的安全检查。
int x = 10;
void* voidPtr = reinterpret_cast<void*>(&x); // 将int指针转换为void指针Derived* derivedPtr = new Derived();
Base* basePtr = reinterpret_cast<Base*>(derivedPtr); // 将派生类指针转换为基类指针
  • const_cast:用于去除表达式的常量性,可以将const或volatile限定符添加或删除。const_cast主要用于修改指针或引用的底层const属性,但是不能用于修改本身是常量的对象。
const int x = 10;
int* nonConstPtr = const_cast<int*>(&x); // 去除常量性,但修改非常量对象是未定义行为const int y = 20;
int& nonConstRef = const_cast<int&>(y); // 去除常量性,但修改非常量对象是未定义行为

需要注意的是,尽管强制类型转换运算符可以用于特定的类型转换,但过度使用它们可能会导致代码难以理解和维护。因此,在使用强制类型转换运算符时,应当谨慎并确保转换的合理性和安全性。

2.简述类成员函数的重写、重载和隐藏的区别

在C++中,类成员函数的重写、重载和隐藏是面向对象编程中常用的概念,它们的含义和使用方式如下:

  • 重写:在派生类中,如果基类的虚函数(virtual function)在派生类中有相同的函数签名(即函数名和参数类型),那么基类的虚函数被派生类的函数重写。这种情况下,通过基类的指针或引用调用该函数时,会执行派生类的函数,这是多态性的一种体现。
class Base {
public:virtual void func() { cout << "Base::func()" << endl; }
};class Derived : public Base {
public:void func() override { cout << "Derived::func()" << endl; } // 重写基类的func()函数
};
  • 重载:在同一个类中,如果有两个或多个函数名相同,但参数列表(包括参数数量和类型)不同,那么这些函数就构成了重载函数。函数的返回类型不能作为重载的依据。
class MyClass {
public:void func() { cout << "func()" << endl; } // func()函数的一个版本void func(int x) { cout << "func(int)" << endl; } // func()函数的另一个版本,形成重载
};
  • 隐藏:在派生类中,如果存在与基类同名但不同参数列表的函数,那么基类中的同名函数在派生类中会被隐藏。这意味着你不能通过派生类的对象或指针来调用被隐藏的基类函数,除非显式地通过基类的名字调用。
class Base {
public:void func() { cout << "Base::func()" << endl; }
};class Derived : public Base {
public:void func(int x) { cout << "Derived::func(int)" << endl; } // 隐藏了基类的func()函数
};

在这个例子中,如果你创建了一个Derived对象,并尝试调用func()函数(没有参数),编译器会报错,因为Derived::func(int)隐藏了Base::func()

这三种概念在实际编程中应用频繁,理解它们对于编写和理解面向对象的C++代码至关重要。

3.类型转换分为哪几种?各自有什么样的特点?

在C++中,类型转换主要分为以下几种类型:

  • 隐式类型转换(Implicit Type Conversion):编译器自动进行的类型转换。例如,从小的数值类型(如int)转换为大的数值类型(如double),或者从派生类转换为基类。隐式转换通常在赋值、函数调用、算术运算和逻辑运算等操作中发生。
int x = 10;
double y = x; // 隐式转换,从int转换为doubleclass Base {};
class Derived : public Base {};
Derived d;
Base& b = d; // 隐式转换,从Derived转换为Base
  • 显式类型转换(Explicit Type Conversion):由程序员显式进行的类型转换,使用C++的强制类型转换运算符,如static_cast、dynamic_cast、reinterpret_cast和const_cast。
int x = 10;
double y = static_cast<double>(x); // 显式转换,使用static_cast从int转换为doubleBase* basePtr = new Derived();
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 显式转换,使用dynamic_cast从Base*转换为Derived*
  • 构造函数和类型转换函数的转换:C++允许通过构造函数和类型转换函数进行类型转换。一个单参数的构造函数定义了如何从参数类型转换为类类型,而类型转换函数定义了如何从类类型转换为其他类型。
class MyComplex {
public:MyComplex(double real, double imag = 0) {} // 构造函数,定义了如何从double转换为MyComplexoperator double() {} // 类型转换函数,定义了如何从MyComplex转换为double
};double d = 1.0;
MyComplex c = d; // 通过构造函数进行转换double d2 = static_cast<double>(c); // 通过类型转换函数进行转换

以上三种类型转换有各自的特点和适用场合。隐式类型转换简单方便,但可能导致精度丢失或意料之外的结果。显式类型转换提供了更多的控制,但需要程序员确保转换的安全性。构造函数和类型转换函数的转换则相对灵活,可以自定义转换规则,但需要更多的代码来实现。

4.RTTI是什么?其原理是什么?

RTTI,全称是”Run-Time Type Identification”,中文叫做”运行时类型信息”。它是C++的一个特性,它能让我们在程序运行的时候获取到对象的类型信息,或者说是能让我们知道一个对象是什么类型的。

RTTI的工作原理主要有两个方面:

  • type_info:这是一个类,当我们用typeid操作符去获取一个对象的类型信息时,就会得到这个类的一个实例。这个实例包含了类型的一些信息,比如类型的名字。
  • dynamic_cast:这是一个转换操作符,我们可以用它来在运行时检查一个对象是否能被安全的转换到某个类型。

举个例子吧,比如我们有个动物的基类Animal,然后有两个派生类DogCat。我们有一个Animal的指针,但是我们不知道它实际指向的是Dog还是Cat

如果我们想要调用Dog或者Cat特有的方法,我们就需要知道这个指针实际指向的是哪个类。这时候,我们就可以利用RTTI的dynamic_cast来检查这个指针能不能被转换到DogCat,如果能,那就说明这个指针指向的就是那个类。

Animal* myAnimal = getAnimalSomehow(); // 这个函数返回一个Animal指针,但是我们不知道它实际指向的是Dog还是Cat。Dog* myDog = dynamic_cast<Dog*>(myAnimal);
if (myDog != nullptr) {// 如果myDog不是nullptr,说明myAnimal实际指向的是Dog,那么我们就可以安全的调用Dog的方法了。myDog->bark();
} else {// 否则,myAnimal可能指向的是Cat。Cat* myCat = dynamic_cast<Cat*>(myAnimal);if (myCat != nullptr) {// 如果myCat不是nullptr,说明myAnimal实际指向的是Cat,那么我们就可以安全的调用Cat的方法了。myCat->meow();}
}

这样,我们就可以利用RTTI在运行时确定对象的类型,然后做出相应的操作。

5.c++中四种cast转换

在C++中,有四种类型的类型转换运算符,或者说是”cast”。这四种转换运算符分别是:static_castdynamic_castconst_castreinterpret_cast。每一种都有它们自己的用途和限制。

  • static_cast:这是最常用的类型转换操作符。它可以在各种不同类型之间进行转换,包括基本数据类型,指针,引用等等。但是,它不能去掉const属性,也不能在没有相关继承关系的类之间进行转换。

    例如,我们可以把一个double类型的数转换为int类型:

double value = 3.14;
int intValue = static_cast<int>(value); // intValue 现在是 3
  • dynamic_cast:这个转换操作符主要用在多态类型的对象上,也就是在有继承关系的类之间进行转换。它在运行时检查转换的安全性。如果转换不安全,比如把父类对象转换为子类类型,那么转换就会失败,返回一个nullptr。

  • const_cast:这个转换操作符主要用来去掉对象的const属性。但是要注意,去掉const属性并修改对象的值是未定义行为,可能会导致程序崩溃。

    例如,我们可以去掉一个const int的const属性:

const int value = 10;
int* mutableValue = const_cast<int*>(&value);

reinterpret_cast:这个转换操作符可以在任何两种类型之间进行转换,包括指针,引用,基本数据类型等等。但是,它是最不安全的转换操作符,因为它会直接进行二进制的转换,不会进行任何类型的检查。

例如,我们可以把一个int类型的数转换为一个指针:

int value = 42;
void* pointer = reinterpret_cast<void*>(value);

这四种类型转换操作符都有各自的用途和限制,使用时需要谨慎,确保转换的安全性。


http://www.ppmy.cn/devtools/23503.html

相关文章

主成分分析(PCA):揭秘数据的隐藏结构

在数据分析的世界里&#xff0c;我们经常面临着处理高维数据的挑战。随着维度的增加&#xff0c;数据处理、可视化以及解释的难度也随之增加&#xff0c;这就是所谓的“维度的诅咒”。主成分分析&#xff08;PCA&#xff09;是一种强大的统计工具&#xff0c;用于减少数据的维度…

Vue3+Nuxt3 从0到1搭建官网项目(SEO搜索、中英文切换、图片懒加载)

Vue2Nuxt2 从 0 到1 搭建官网~ 想开发一个官网&#xff0c;并且支持SEO搜索&#xff0c;当然离不开我们的 Nuxt &#xff0c;Nuxt2 我们刚刚可以熟练运用&#xff0c;现在有出现了Nuxt3&#xff0c;那通过本篇文章让我们一起了解一下。 安装 Nuxt3 // npx nuxilatest init &…

Blender曲线操作

1.几种常见建模方式 -多边形建模&#xff1a;Blender&#xff0c;C4D&#xff0c;3DsMax&#xff0c;MaYa -曲线&#xff1a; -曲面&#xff1a;Rhino&#xff08;Nurbs&#xff09; -雕刻&#xff1a;Blender&#xff0c;ZBrush -蜡笔&#xff1a;Blender 1&#xff09;新…

清除git缓存后,每次pull或者push都需要输入用户名密码

git bash进入你的项目目录&#xff0c;输入&#xff1a;git config --global credential.helper store 然后在文件下pull一下&#xff0c;输入一次用户名密码后&#xff0c;再次pull或者push就不需要输入了。 亲测有用哦

黑马点评(十二) -- UV统计

一 . UV统计-HyperLogLog 首先我们搞懂两个概念&#xff1a; UV&#xff1a;全称Unique Visitor&#xff0c;也叫独立访客量&#xff0c;是指通过互联网访问、浏览这个网页的自然人。1天内同一个用户多次访问该网站&#xff0c;只记录1次。 PV&#xff1a;全称Page View&…

道可云元宇宙每日资讯|苹果推出开放语言模型OpenELM

道可云元宇宙每日简报&#xff08;2024年4月26日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 工信部发布中关村园区建设方案&#xff1a;加快突破元宇宙核心技术 4 月 25 日&#xff0c;工业和信息化部、科学技术部、北京市人民政府印发了《中关村世界领先科技…

Golang | Leetcode Golang题解之第55题跳跃游戏

题目&#xff1a; 题解&#xff1a; // 贪心算法 func canJump(nums []int) bool {cover : 0n : len(nums)-1for i : 0; i < cover; i { // 每次与覆盖值比较cover max(inums[i], cover) //每走一步都将 cover 更新为最大值if cover > n {return true}}return false } …

uniapp实现点击事件跳转页面

首先定义一个点击事件 这里采用的vue3的写法&#xff0c;然后写上触发事件后要跳转的路径 function jump() {uni.switchTab({url:/pages/bangong/index})} 到这里就简单的实现uniapp的点击跳转页面了