C++面向对象的常见面试题目(一)

1. 面向对象的三大特征

(1)封装:隐藏对象的内部状态,只暴露必要的接口。

#include <iostream>
#include <string>

// 定义一个简单的类 Person
class Person {
private: // 私有成员,外部不可直接访问
    std::string name;
    int age;

public: // 公共方法,外部可以访问
    // 构造函数,用于初始化对象
    Person(std::string n, int a) {
        name = n;
        age = a;
    }

    // 公共方法,用于设置年龄
    void setAge(int a) {
        if (a > 0 && a < 150) { // 简单的年龄验证
            age = a;
        } else {
            std::cout << "Invalid age!" << std::endl;
        }
    }

    // 公共方法,用于获取姓名
    std::string getName() {
        return name;
    }

    // 公共方法,用于获取年龄
    int getAge() {
        return age;
    }
};

int main() {
    // 创建 Person 对象
    Person p1("Alice", 30);

    // 尝试直接访问私有成员(编译错误)
    // std::cout << p1.name << std::endl;

    // 使用公共方法设置年龄
    p1.setAge(35);

    // 使用公共方法获取姓名和年龄
    std::cout << "Name: " << p1.getName() << ", Age: " << p1.getAge() << std::endl;

    return 0;
}

(2)继承:无需修改原有类的情况下对功能实现拓展

#include <iostream>
#include <string>

// 定义一个基类 Animal
class Animal {
protected: // 受保护的成员,子类可以访问
    std::string name;

public:
    // 构造函数,初始化 Animal 的名称
    Animal(std::string n) : name(n) {}

    // 公共方法,用于输出 Animal 的声音
    void makeSound() const {
        std::cout << "Animal " << name << " makes a sound" << std::endl;
    }
};

// 定义一个派生类 Dog,继承自 Animal
class Dog : public Animal {
private:
    std::string breed;

public:
    // 构造函数,初始化 Dog 的名称和品种
    Dog(std::string n, std::string b) : Animal(n), breed(b) {}

    // 重写父类的 makeSound 方法
    void makeSound() const {
        std::cout << "Dog " << name << " barks" << std::endl;
    }

    // 新的方法,用于输出 Dog 的品种
    void showBreed() const {
        std::cout << "Dog " << name << " is of breed " << breed << std::endl;
    }
};

int main() {
    // 创建 Animal 对象
    Animal a("Generic");

    // 调用 Animal 的方法
    a.makeSound();

    // 创建 Dog 对象
    Dog d("Buddy", "Labrador");

    // 调用 Dog 的方法
    d.makeSound();
    d.showBreed();

    // 使用基类指针指向派生类对象,演示多态
    Animal* animalPtr = &d;
    animalPtr->makeSound(); // 调用的是 Dog 的 makeSound 方法

    return 0;
}

(3)多态:同一个函数名在不同对象中有不同的行为。通过时下接口重用增强可拓展性

多态分为2类,静态多态和动态多态。

静态多态:又称编译时多态。通过函数重载和运算符重载实现。

动态多态:又称运行时多态,通过虚函数和继承实现。

虚函数:在基类中声明函数为虚函数,派生类可以覆盖这些虚函数,从而实现不同的行为。在运行时,根据对象的实际类型决定调用哪个版本的函数,这种机制称为动态绑定或后期绑定。

// 静态多态
#include <iostream>

class MathTool {
public:
    // 计算整数平方
    int calculateSquare(int number) {
        return number * number;
    }

    // 计算浮点数平方
    double calculateSquare(double number) {
        return number * number;
    }
};

int main() {
    MathTool tool;

    // 静态多态示例:根据参数类型自动选择合适的方法
    std::cout << "整数5的平方是: " << tool.calculateSquare(5) << std::endl;      // 调用整数版本
    std::cout << "浮点数2.5的平方是: " << tool.calculateSquare(2.5) << std::endl; // 调用浮点数版本

    return 0;
}

// 动态多态
#include <iostream>

// 基类,含有虚函数
class Animal {
public:
    virtual ~Animal() {} // 虚析构函数,确保通过基类指针删除派生类对象时能正确调用派生类的析构函数
    virtual void makeSound() {
        std::cout << "Some animal makes a sound." << std::endl;
    }
};

// 派生类1
class Dog : public Animal {
public:
    void makeSound() override {
        std::cout << "Dog barks." << std::endl;
    }
};

// 派生类2
class Cat : public Animal {
public:
    void makeSound() override {
        std::cout << "Cat meows." << std::endl;
    }
};

int main() {
    Animal* animalPtr; // 基类指针

    // 动态分配内存给派生类对象
    animalPtr = new Dog();
    animalPtr->makeSound(); // 运行时动态决定调用Dog的makeSound()

    animalPtr = new Cat();
    animalPtr->makeSound(); // 运行时动态决定调用Cat的makeSound()

    delete animalPtr; // 由于基类析构函数为虚函数,可以安全删除派生类对象

    return 0;
}

2. 多态的实现原理

(1)静态多态

原理:函数名修饰。编辑器会根据函数的签名(包括参数类型、数量和顺序)对内部的函数名称进行编码,生成一个唯一的标识符。这个编码后的名称包含了足够的信息,使得编译器能够准确地区分不同的重载版本,即使它们的外部名称相同。

编译过程:

        预编译:把头文件中的函数声明拷贝到源文件,避免编译过程中语法分析找不到函数定义。

        编译:语法分析,同时进行符号汇总

        汇编:生成函数名到函数地址的映射,方便之后通过函数名照到函数定义从而执行函数。

        链接:讲过个文件的符号表汇总合并

早绑定:编译器编译时就已经确定对象调用的函数的地址。静态多态依赖于早绑定。

(2)动态多态

原理:虚函数重写。当一个类中声明了至少一个虚函数时,编译器会为该类生成一个虚函数表。这是一个存储虚函数指针的数组,每个指针指向类中相应虚函数的实现。含有虚函数的类实例在内存中除了包含数据成员外,还会有一个指向其所属类的虚函数表的指针(通常称为vptr)。这个vptr是在对象创建时由编译器自动初始化的。在派生类中,当你定义了一个与基类中虚函数同名且签名相同的函数时,这就是虚函数的重写。派生类的虚函数表中,对应的条目会存储派生类中该函数的地址,而非基类的。

虚函数重写:基类函数上加上virtual关键字,在派生类重写虚函数。运行时会根据对象的类型调用相应的函数。如果对象的类型是基类,那么调用基类的函数,如果对象的类型是派生类诶,则调用派生类的函数

晚绑定:程序运行时才确定对象调用的函数的地址。动态多态依赖于晚绑定。C++中,晚绑定是通过virtual关键字实现的。

3. 怎么解决菱形继承

因为c++有多重继承的特性,导致一个子类可能继承多个基类。这些基类可能继承自相同的基类,从而造成了菱形继承。

菱形继承的问题主要是数据冗余并且造成二义性。

二义性:当一个派生类(我们称之为D)从两个不同的基类(比如B1B2)继承,而这两个基类又都继承自同一个基类(A),那么在D中直接访问从A继承来的成员时,编译器无法确定应该使用B1继承的版本还是B2继承的版本。这种情况下,编译器会报错,指出存在二义性。

数据冗余:如果不使用虚拟继承(virtual关键字),每个派生类(B1B2)都会包含基类A的一个完整副本。因此,当D继承自B1B2时,它将拥有两份A的成员,导致数据冗余。这不仅浪费存储空间,还可能导致逻辑上的混乱,尤其是在修改这些成员时,可能会忘记更新所有副本,从而引发一致性问题。

使用虚继承可以解决菱形继承问题。在派生类对共同基类进行虚继承时,编译器会确保只有一份共同基类的实例,而不是每个中间类都有自己的一份。

4. 关键字override、final的作用

子类继承基类的虚函数之后,可能存在这样的问题:子类不希望这个函数会自己的子类被进一步重写;子类想重写一个新函数,但是错误的重写了基类虚函数;子类本意是重写基类的虚函数但是签名不一致导致重新构建了一个虚函数;子类希望自己绝后!但是没有办法从语法上完成这件事。

override可以指定子类的一个虚函数复写基类的一个虚函数;并且保证该重写的虚函数与子类的虚函数有相同的签名。

final可以指定某个虚函数不能在派生类中覆盖,或者某个类不能被派生。加在类前面就可以阻塞类的进一步派生。

5. C++类型推导的用法

C++类型推导主要有三个方面:auto、decltype、模板类型推导

(1)auto:用于推导变量的类型,通过强制声明一个变量的初始值,编译器会通过初始值进行推导类型

(2) decltype

用于推导表达式的类型,编译器只分析表达式类型而不参与运算

 6. C++11 中function、lambda、bind之间的关系

std::function是一个抽象了函数参数和函数返回值的类模板。它把任意函数包装成了一个对象,该对象可以保存传递以及复制。也可以进行动态绑定,只需要修改该对象,实现类似多态的效果

#include <iostream>
#include <functional>

// 函数对象(仿函数)
struct Adder {
    int operator()(int a, int b) const {
        return a + b;
    }
};

// 普通函数
int subtract(int a, int b) {
    return a - b;
}

// 成员函数
struct Calculator {
    int multiply(int a, int b) {
        return a * b;
    }
};

int main() {
    // 使用 std::function 声明不同类型的可调用对象
    std::function<int(int, int)> func1 = Adder();  // 函数对象
    std::function<int(int, int)> func2 = subtract; // 普通函数

    Calculator calc;
    std::function<int(Calculator&, int, int)> func3 = &Calculator::multiply;  // 成员函数

    // 调用并输出结果
    std::cout << "Adder result: " << func1(10, 5) << std::endl;
    std::cout << "Subtract result: " << func2(10, 5) << std::endl;
    std::cout << "Multiply result: " << func3(calc, 10, 5) << std::endl;

    return 0;
}

lambda表达式:一种方面的创建匿名函数的语法糖

原理:编译的时候把表达式转变为一个函数对象,然后根据表达式惨呼列表重载operate ()

demo:

// 简单表达式
auto sayHello = []{ std::cout << "Hello, World!\n"; };
sayHello();

// 捕获外部变量
int x = 10, y = 20;
auto incrementAndPrint = [x, &y]() {
    x++; // 按值捕获,不会改变外部x
    y++; // 按引用捕获,会改变外部y
    std::cout << "x: " << x << ", y: " << y << "\n";
};
incrementAndPrint(); // 输出:x: 11, y: 21
std::cout << "After: x=" << x << ", y=" << y << "\n"; // 输出:x=10, y=21


// 指定返回类型
int x = 10, y = 20;
auto incrementAndPrint = [x, &y]() {
    x++; // 按值捕获,不会改变外部x
    y++; // 按引用捕获,会改变外部y
    std::cout << "x: " << x << ", y: " << y << "\n";
};
incrementAndPrint(); // 输出:x: 11, y: 21
std::cout << "After: x=" << x << ", y=" << y << "\n"; // 输出:x=10, y=21


std::bind:用来通过绑定函数以及函数参数的方式生成函数对象的模板函数,提供占位符,实现灵活的参数绑定

#include <iostream>
#include <functional>

// 通用加法函数
int add(int x, int y) {
    return x + y;
}

int main() {
    // 使用std::bind固定add的第一个参数为5
    auto addFive = std::bind(add, 5, std::placeholders::_1);

    // 现在addFive是一个新的可调用对象,只需要一个参数
    std::cout << "Adding 5 to 3 gives: " << addFive(3) << '\n'; // 输出: 8
    std::cout << "Adding 5 to 10 gives: " << addFive(10) << '\n'; // 输出: 15

    return 0;
}

总结:function 用来描述函数对象的类型;lambda 表达式用来生成函数对象(可以访问外部变量的匿名函数);bind 也是用来生成函数对象(函数和参数进行绑定生成函数对象);

这是一条吃饭博客,由挨踢零声赞助。学C/C++就找挨踢零声,加入挨踢零声,面试不挨踢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/782321.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

通俗易懂的信道复用技术详解:频分、时分、波分与码分复用

在现代通信网络中&#xff0c;信道复用技术 扮演着至关重要的角色。今天&#xff0c;我们将用通俗易懂的语言来讲解几种常见的信道复用技术&#xff1a;频分复用、时分复用、波分复用 和 码分复用。这篇文章特别适合基础小白&#xff0c;希望能帮助你快速理解这些概念。 一、频…

Bean的管理

1.主动获取Bean spring项目在需要时&#xff0c;会自动从IOC容器中获取需要的Bean 我们也可以自己主动的得到Bean对象 &#xff08;1&#xff09;获取bean对象&#xff0c;首先获取SpringIOC对象 private ApplicationContext applicationContext //IOC容器对象 (2 )方法…

[算法] 优先算法(四):滑动窗口(下)

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

Springboot 敏感词过滤

参考&#xff1a;网站是怎么屏蔽脏话的呢&#xff1a;简单学会SpringBoot项目敏感词、违规词过滤方案_springboot 项目关键词过滤-CSDN博客 【敏感词过滤】_wx60d2a462203aa的技术博客_51CTO博客 1、添加依赖 <dependency><groupId>com.github.houbb</groupI…

模型训练之数据集

我们知道人工智能的四大要素&#xff1a;数据、算法、算力、场景。我们训练模型离不开数据 目标 一、数据集划分 定义 数据集&#xff1a;训练集是一组训练数据。 样本&#xff1a;一组数据中一个数据 特征&#xff1a;反映样本在某方面的表现、属性或性质事项 训练集&#…

输入Rviz打不开,显示could not contact Ros master at[..],retrying

直接输入rviz会报错无法打开 解决方法&#xff1a; 先输入roscore&#xff0c;再用ctrlaltt打开新终端&#xff0c;在新终端输入rviz/rosrun rviz rviz即可

深度学习3 基于规则的决策树模型

1.决策树是一种归纳学习算法&#xff0c;从一些没有规则、没有顺序、杂乱无章的数据中&#xff0c;推理出决 策模型。不管是什么算法的决策树&#xff0c;都是一种对实例进行分类的树形结构。决策树有三个要素&#xff1a;节点(Node)、分支(Branches)和结果(Leaf)。 训练决策树…

二、Spring

二、Spring 1、Spring简介 1.1、Spring概述 官网地址&#xff1a;https://spring.io/ Spring 是最受欢迎的企业级 Java 应用程序开发框架&#xff0c;数以百万的来自世界各地的开发人员使用 Spring 框架来创建性能好、易于测试、可重用的代码。 Spring 框架是一个开源的 Jav…

VMware Workstation Pro 17.5.2 + license key

Workstation Pro是专为Windows操作系统设计的功能强大的虚拟化软件平台,它允许用户在其计算机上创建和运行虚拟机,这使他们能够同时与多个操作系统、应用程序和开发环境一起工作。 Workstation Pro的主要特点之一是其易用性,程序提供了直观的界面,允许用户轻松创建、配置和…

JCR一区 | Matlab实现GAF-PCNN-MATT、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断

JJCR一区 | Matlab实现GAF-PCNN-MATT、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断 目录 JJCR一区 | Matlab实现GAF-PCNN-MATT、GASF-CNN、GADF-CNN的多特征输入数据分类预测/故障诊断分类效果格拉姆矩阵图GAF-PCNN-MATTGASF-CNNGADF-CNN 基本介绍程序设计参考资料 分…

Ubuntu24.04清理常见跟踪软件tracker

尽量一天一更&#xff0c;不刷视频&#xff0c;好好生活 打开系统监视器&#xff0c;发现开机有个tracker-miner-fs-fs3的跟踪程序&#xff0c;而且上传了10kb的数据。 搜索知&#xff0c;该程序会搜集应用和文件的信息。 删除tracker 显示带tracker的apt程序 sudo apt lis…

【Excel】 给证件照换底色

1. 双击图片 → 删除背景 2. 标记要保留的区域 → 标记 → 保留更改 3. 重新设置背景色

最新整理的机器人相关数据合集(1993-2022年不等 具体看数据类型)

机器人安装数据是指记录全球或特定区域内工业机器人新安装数量的信息&#xff0c;这一数据由国际机器人联合会(IFR)等权威机构定期发布。这些数据不仅揭示了机器人技术的市场需求趋势&#xff0c;还反映了各国和地区自动化水平及产业升级的步伐。例如&#xff0c;数据显示中国在…

基于Java+SpringMvc+Vue技术的图书管理系统的设计与实现(60页论文参考)

博主介绍&#xff1a;硕士研究生&#xff0c;专注于Java技术领域开发与管理&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架构思想、较扎实的技术功底和资深的项目管理经…

回顾 DTC 2024 大会——聚焦数据技术创新:揭秘下一代纯实时搜索引擎 INFINI Pizza

2024 年 4 月 12 日至 13 日&#xff0c;备受瞩目的第十三届“数据技术嘉年华”&#xff08;DTC2024&#xff09;在北京新云南皇冠假日酒店盛大开幕。本次大会由中国 DBA 联盟&#xff08;ACDU&#xff09;与墨天轮社区联合主办&#xff0c;以“智能云原生一体化——DB 与 AI 协…

28个常用的损失函数介绍以及Python代码实现总结

28个常用的损失函数介绍以及Python代码实现总结 最近在做多分类的研究&#xff0c;总是遇到这么多损失函数&#xff0c;应该挑选哪一个损失函数呢&#xff1f;这样的问题。于是心血来潮便想着对损失函数进行总结。 以下是一个预览总结&#xff1a; 损失函数名称问题类型L1范…

缓冲器的重要性,谈谈PostgreSQL

目录 一、PostgreSQL是什么二、缓冲区管理器介绍三、缓冲区管理器的应用场景四、如何定义缓冲区管理器 一、PostgreSQL是什么 PostgreSQL是一种高级的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它以其稳定性、可靠性和高度可扩展性而闻名。它最初由加…

网络安全设备——防火墙

网络安全设备防火墙是一种用来加强网络之间访问控制的特殊网络互联设备。以下是对防火墙的详细解释&#xff1a; 一、定义与基本概念 定义&#xff1a;防火墙是指设置在不同网络&#xff08;如可信任的企业内部网和不可信的公共网&#xff09;或网络安全域之间的一系列部件的…

EOF 为 (End Of File) 的缩写 , 值通常为 -1

EOF是一个计算机术语&#xff0c;为 End Of File 的缩写 EOF 的值通常为 -1 EOF 的值通常为 -1&#xff0c;但它依系统有所不同。巨集 EOF会在编译原始码前展开实际值给预处理器。 与 feof 与 feof C语言中&#xff0c;当把数据以二进制形式存放到文件中时&#xff0c;就会有…

YOLO-World实时开集检测论文阅读

论文&#xff1a;《YOLO-World: Real-Time Open-Vocabulary Object Detection》 代码&#xff1a;https://github.com/AILab-CVC/YOLO-World 1.Abstract 我们介绍了YOLO World&#xff0c;这是一种创新的方法&#xff0c;通过在大规模数据集上进行视觉语言建模和预训练&#…