C++ 基础知识 面试题(一)

这篇具有很好参考价值的文章主要介绍了C++ 基础知识 面试题(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.变量的声明与定义

声明:int x; //告诉编译器这个变量的类型和名称

定义:int x = 0; //告诉编译器这个变量的类型和名称,为该变量分配内存空间,并初始化该变量

主要区别在于是否为变量分配内存空间

2.extern关键字

用法一:在声明时修饰函数或变量,表明该函数或变量定义其他文件中,告诉编译器去其他文件中寻找函数或变量的定义。

extern int a;

extern void func();

当一个变量要在多个文件中使用时,只需在一个文件中定义该变量,然后再其他文件中使用extern声明这个变量。

用法二:extern "C" 

C++会对函数名进行名称修饰(Name Mangling),以便支持函数重载和命名空间等特性。

也就是说,编译器会在编译函数的过程中将函数的参数类型也加到函数名后面。

但是C语言不支持函数重载,编译时不会加上函数的参数类型,一般只有函数名。

所以当我们要在C++中调用C语言的代码时,不能直接按照C++的规则进行名称修饰。

所以可以在前面加上extern "C" ,表明这一段是C语言代码,按照C语言的规则编译。

Example:

C++中直接使用C代码:

// myfunc.cpp
#include <stdio.h>

extern "C" {
    void myFunc() {
        printf("Hello from myFunc!\n");
    }
}

C++中调用C语言头文件:

// main.cpp
#include <iostream>

extern "C" {
    #include "mylib.h"
}

int main() {
    myFunc();
    std::cout << "myVar = " << myVar << std::endl;
    return 0;
}

3.内存类型

栈内存(Stack Memory):栈内存是由编译器自动分配和释放的,用于存储局部变量和函数参数等数据。栈内存的大小是固定的,通常比堆内存小,且分配和释放速度较快。当函数执行完毕时,栈内存中的数据会自动被销毁。

堆内存(Heap Memory):堆内存是由程序员手动分配和释放的,用于存储动态分配的数据。堆内存的大小是不固定的,可以根据需要进行动态调整,但分配和释放速度较慢。需要注意的是,堆内存中的数据需要手动释放,否则会导致内存泄漏。

全局内存(Global Memory):全局内存是由编译器自动分配和释放的,用于存储全局变量和静态变量等数据。全局内存的大小是固定的,程序运行期间始终存在,直到程序结束才会被释放。

常量内存(Constant Memory):常量内存是用于存储常量数据的内存区域,通常包括字符串常量、枚举常量和const修饰的变量等。常量内存的大小是固定的,程序运行期间始终存在,直到程序结束才会被释放。

关于内存类型,还会涉及到自由存储区

自由存储区是一个抽象的概念,指的是C++中通过new()和delete()动态分配和释放的内存区域。

C++编译器默认使用堆来实现自由存储区,但是自由存储区不等价于堆。

堆是操作系统维护的一块动态分配内存,通过malloc()和free()来分配和释放。

4.C++程序编译的过程:

  • 预处理:C++编译器会先对源代码进行预处理,将所有的#include指令替换为对应的头文件内容,宏定义替换为对应的文本等。
  • 编译:编译器将预处理后的源代码翻译成汇编代码,这个过程包括词法分析、语法分析、语义分析等步骤。
  • 汇编:汇编器将汇编代码转换为机器码,这个过程包括生成目标文件、符号解析、重定位等步骤。
  • 链接:连接器将目标文件和库文件链接在一起,生成可执行文件。这个过程包括符号解析、重定位等步骤。

编译过程中生成的文件流程图:

C++ 基础知识 面试题(一),C++,c++,面试

5. malloc和new的区别

语法:C语言中使用malloc函数动态分配内存,而C++中使用new运算符动态分配内存

参数:malloc函数只接受一个参数,即要分配的内存大小,而new运算符可以接受一个参数(分配单个对象内存)或多个参数(分配数组内存)

        eg: Person* arr = new Person[size] { {"Tom", 20}, {"Jerry", 18}, {"Alice", 22} };

返回值:malloc函数返回void*类型的指针,需要进行强制类型转换才能使用;new运算符返回分配的对象类型的指针,不需要进行强制类型转换

内存初始化:malloc函数分配的内存不会被初始化,而new运算符分配的内存会被初始化为默认值(0或空指针)或用户指定的值

异常处理:malloc函数在分配内存失败时返回空指针,new运算符在分配内存失败时会抛出std::bad_alloc异常

6.计算机内部存储负数的方式

最常用的方式是二进制补码表示法。

在二进制补码表示法中,最高位(即最左边的位)为符号位,0表示正数,1表示负数。

例如,十进制数-42的8位二进制表示为11010110。

其计算方法为,先用原码表示-42,即将42的二进制(00101010)表示取反,得到11010101,然后将结果加1,得到11010110,这就是-42的二进制补码表示。

7.计算机内部存储浮点数的方式

计算机内部存储浮点数的方式是采用IEEE 754标准。

在IEEE 754标准中,一个浮点数由三个部分组成:符号位、指数和尾数。

IEEE 754标准定义了两种浮点数表示方式:单精度浮点数(4个字节)和双精度浮点数(8个字节)。

在单精度浮点数中,第1位为符号位,接下来的8位为指数部分,最后的23位为尾数部分。

C++ 基础知识 面试题(一),C++,c++,面试

在双精度浮点数中,第1位为符号位,接下来的11位为指数部分,最后的52位为尾数部分。

C++ 基础知识 面试题(一),C++,c++,面试

对于指数部分,IEEE 754标准采用了偏移值的表示方法。具体来说,指数部分的实际值等于指数字段的值减去一个偏移值。

在单精度浮点数中,偏移值为127,在双精度浮点数中,偏移值为1023。

对于尾数部分,IEEE 754标准采用了规格化和非规格化的表示方法。

规格化的尾数部分表示的是一个范围在1到2之间的实数,非规格化的尾数部分表示的是一个范围在0到1之间的实数。

举例:单精度浮点数

例如,我们要存储十进制数-3.25,其二进制表示为-11.01(整数部分除2取余再翻转,小数部分乘2取整)

根据IEEE 754标准,我们需要将其转换为科学计数法的形式,即-1.101 x 2^1。

其中,符号位为1,指数部分为1+127=128(偏移值为127),尾数部分为10100000000000000000000。

将这三部分按照规定的格式组合起来,就可以得到单精度浮点数的二进制表示:1 10000000 10100000000000000000000。其中,第1位为符号位,接下来的8位为指数部分,最后的23位为尾数部分。

8.内存泄漏

内存泄漏是指程序在运行过程中申请的内存空间没有被及时释放,导致系统中的可用内存不断减少,最终可能导致系统崩溃或性能下降的问题。

以下是一些常用的方法:

  • 使用内存分配和释放函数时要小心,确保申请的内存空间在不需要时及时释放。
  • 避免使用全局变量,因为全局变量的生命周期很长,容易导致内存泄漏。
  • 使用智能指针和垃圾回收机制来管理内存,这样可以自动释放不再使用的内存空间。
  • 使用静态分析工具来检测潜在的内存泄漏问题。
  • 对于C++等语言,可以使用RAII技术,即在对象的构造函数中申请内存,在析构函数中释放内存,从而确保内存的正确释放。
  • 在对指针赋值前,要考虑是否释放指针原先指向的内存。

9.volatil关键字

用于告诉编译器一个变量可能会被意外地修改。

它的作用是防止编译器对变量的优化,保证程序的正确性。

当一个变量被声明为volatile时,编译器将不会对该变量进行优化,例如,不会将该变量的值缓存到CPU寄存器中。这是因为该变量的值可能会被其他线程或硬件设备修改,如果编译器对该变量进行了优化,就可能导致程序出错。

10. final关键字

用于修饰类、成员函数和虚函数。它的作用是禁止继承、重载和覆盖。

当一个类被声明为final时,它不能被其他类继承。

当一个成员函数被声明为final时,它不能被子类重载或覆盖。

当一个虚函数被声明为final时,它不能被子类覆盖。

当一个变量被声明为final时,它只能赋一次值。

当一个引用变量被声明为final时,它只能赋一次值,并且它只能永远指向该对象,无法指向其他对象。虽然final的引用指向对象A后,不能再重新指向对象B,但是对象A内部的数据可以被修改。

final修饰的实例变量一般添加 static修饰。static final联合修饰的变量称为常量。
 

11.多态

静态多态和动态多态都是面向对象编程中的概念,它们都涉及到多态性的实现。

静态多态是指在编译时就能够确定调用的函数,也称为编译时多态。它是通过函数重载和运算符重载实现的,编译器会根据参数的类型和数量来决定调用哪个函数或运算符。静态多态的优点是效率高,因为编译器能够在编译时确定函数调用,不需要在运行时进行类型检查。

动态多态是指在运行时才能够确定调用的函数,也称为运行时多态。

它允许同一个函数名在不同的对象中具有不同的行为。多态的实现原理是通过虚函数来实现的。

在C++中,通过在函数声明前面添加关键字virtual来将函数声明为虚函数。当一个类中的函数被声明为虚函数时,它可以被子类重写。

使用虚函数可以使程序更加灵活,可以根据不同的对象类型调用不同的函数实现。

编译器会在基类中为虚函数生成一个虚函数表,派生类会继承这个虚函数表并重写其中的虚函数。虚函数的实现依赖于虚函数表,每个对象都有一个指向虚函数表的指针,通过该指针可以在运行时确定应该调用哪个函数。

通过基类指针或引用调用虚函数(普通变量不会调用虚函数)。在运行时,虚函数会根据对象的实际类型来调用相应的函数,从而实现多态。

#include <iostream>
using namespace std;

class Animal {
public:
    virtual void speak() {
        cout << "This is an animal speaking." << endl;
    }
};

class Cat : public Animal {
public:
    void speak() {
        cout << "Meow!" << endl;
    }
};

class Dog : public Animal {
public:
    void speak() {
        cout << "Woof!" << endl;
    }
};

int main() {
    Animal* animal1 = new Cat();
    Animal* animal2 = new Dog();
    animal1->speak();// it will print "Meow!"
    animal2->speak();// it will print "Woof!"
    return 0;
}

12.引用和指针的区别

  • 指针可以为空,而引用不能。指针可以指向空值,即nullptr,而引用必须引用一个有效的对象。
  • 指针可以进行算术运算,而引用不能。指针可以进行加、减等运算,而引用是引用的变量值加一。
  • 指针是一个实体,需要分配内存空间。引用只是变量的别名,不需要分配内存空间。
  • 引用在定义的时候必须进行初始化,并且不能够改变。指针在定义的时候不一定要初始化,并且可以被重新赋值。
  • 有多级指针,但是没有多级引用,只能有一级引用。
  • sizeof 引用得到的是所指向的变量(对象)的大小,而sizeof 指针得到的是指针本身的大小。
  • 引用在内部实现上是一个指针,但是它在使用时,像一个变量一样使用。这使得使用引用更加方便和直观。
  • 作为参数时也不同,传指针的实质是传值,传递的值是指针的地址;传引用的实质是传地址,传递的是变量的地址。

13.函数重写和重载的区别

函数重写(Overriding):在继承关系中,子类可以对父类的某个方法进行重写,以满足自己的需求。子类重写父类的方法时,方法名、参数列表和返回值类型必须与父类的方法相同,但是方法体可以不同。在运行时,如果调用的是子类对象的该方法,那么就会执行子类中的方法体,而不是父类中的方法体。

在C++中,如果想要在派生类中重写基类的函数,通常需要在基类的函数声明中添加virtual关键字,这样才能实现运行时多态。如果基类函数没有使用virtual关键字,那么在派生类中重写该函数只会隐藏基类的同名函数,而不会实现多态

函数重载(Overloading):在同一个类中,可以定义多个同名的方法,但是它们的参数列表必须不同。参数列表可以包括参数的类型、个数、顺序等。在调用该方法时,编译器会根据传入的参数类型和数量来选择合适的方法进行调用。

总的来说,函数重写是子类对父类某个方法的重新实现,而函数重载是同一个类中对同名方法的多次定义。

14.面向对象编程(Object-Oriented Programming,简称 OOP)具有以下三大特征:

1) 封装(Encapsulation):
   - 封装是将数据和操作数据的方法(即类的成员变量和方法)组合在一起,形成一个称为类的实体。
   - 封装隐藏了数据的具体实现细节,只提供公共接口供外部访问和操作数据,也增加了对数据和方法的访问控制(private, protected, public)
   - 通过封装,可以实现数据的安全性和灵活性,使得代码更加模块化和可维护。

2) 继承(Inheritance):
   - 继承是指一个类(称为子类或派生类)可以从另一个类(称为父类或基类)继承属性和方法。
   - 继承使得子类可以重用父类的代码,避免了重复编写相同的代码。
   - 子类可以扩展或修改从父类继承的属性和方法,也可以添加自己特有的属性和方法。
   - 继承提供了代码的层次化组织,使得代码更加可扩展和可维护。

3) 多态(Polymorphism):
   - 多态是指同一个方法名可以在不同的对象上具有不同的实现方式。
   - 多态通过方法的重写和方法的重载实现。
   - 方法的重写(Override)指子类重写父类的方法,使得子类对象调用该方法时执行子类的实现逻辑。
   - 方法的重载(Overload)指在同一个类中定义多个方法,它们具有相同的方法名但不同的参数列表。
   - 多态提高了代码的灵活性和可扩展性,使得同一段代码可以适用于不同类型的对象。

15.空类

在 C++ 中,空类(Empty Class)指的是没有显式声明任何成员变量或成员函数的类。它是一种最简单的类定义形式,没有任何数据成员或成员函数的定义。空类可以用于一些特定的编程场景,例如作为基类或占位符

下面是一个示例,展示了一个空类的定义:

```cpp
class EmptyClass {
    // Empty class with no member variables or member functions
};
```

在这个示例中,`EmptyClass` 是一个空类,没有任何成员变量或成员函数的定义。

空类的主要用途之一是作为基类,用于派生其他类。通过继承空类,子类可以继承空类的特性,并添加自己的成员变量和成员函数。这种用法在实现多态和组织代码结构时很常见。

另外,空类也可以用作占位符,作为一种标记或占位的作用。例如,在某些设计模式中,可以使用空类作为标记类来表示某个特定的概念或行为。

需要注意的是,即使是空类,编译器在没有显式定义成员函数时,仍会为其生一些默认的成员函数。这些默认生成的成员函数包括:

1. 默认构造函数(Default Constructor):
   - 默认构造函数是没有参数的构造函数。
   - 如果你没有显式定义任何构造函数,编译器会自动生成一个默认构造函数。
   - 默认构造函数用于创建类的对象,并初始化其成员变量。

2. 默认析构函数(Default Destructor):
   - 默认析构函数没有参数。
   - 如果你没有显式定义任何析构函数,编译器会自动生成一个默认析构函数。
   - 默认析构函数用于在对象被销毁时清理资源。

3. 默认拷贝构造函数(Default Copy Constructor):
   - 默认拷贝构造函数用于创建一个新对象,并将其初始化为另一个同类型对象的副本。
   - 如果你没有显式定义任何拷贝构造函数,编译器会自动生成一个默认拷贝构造函数。
   - 默认拷贝构造函数执行逐个成员变量的复制。

4. 默认赋值运算符(Default Assignment Operator):
   - 默认赋值运算符用于将一个对象的值赋给另一个同类型的对象。
   - 如果你没有显式定义任何赋值运算符,编译器会自动生成一个默认赋值运算符。
   - 默认赋值运算符执行逐个成员变量的赋值。

需要注意的是,只有当实际使用这些函数的时候,编译器才会去定义它们。文章来源地址https://www.toymoban.com/news/detail-516460.html

到了这里,关于C++ 基础知识 面试题(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 2023面试八股文 ——Java基础知识

    编程就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并 终得到结果的过程。 为了使计算机能够理解人的意图,人类就必须要将需解决的问题的思路、方法、 和手段通过计算机能够理解的形式告诉计算机,使得计算机能够根据人的指令一 步一步去工作,

    2024年02月12日
    浏览(53)
  • Java面试知识点(全)- Java面试基础部分一

    Java面试知识点(全)https://nanxiang.blog.csdn.net/article/details/130640392 语法基础 封装 利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联

    2024年02月05日
    浏览(46)
  • Java基础-知识点03(面试|学习)

    String 类是Java中用于表示字符串的类。在Java中,字符串是一系列字符的序列,用于表示文本数据。 String类的作用及特性 1、 创建字符串 : 可以通过字符串字面量或使用new来创建字符串对象。 2、 字符串长度 : 可以使用length()方法获取字符串的长度。 3、 字符串连接

    2024年04月15日
    浏览(59)
  • 【面试题】C#面试常见基础知识点整理(附示例代码)

    大家好,这是自己自行整理的c#面试题,方便自己学习的同时分享出来。 相同点 抽象方法和虚方法都可以供派生类重写, 派生类重写父类的方法都要使用override来声明。 不同点 虚方法必须有方法名称和方法实现;抽象方法是只有方法名称,没有方法实现; 虚方法在派生

    2024年02月02日
    浏览(55)
  • JAVA后端开发面试基础知识(一)——JVM

    Class loader(类装载) 根据给定的全限定名类名(如: java.lang.Object)来装载class文件到 Runtime data area中的method area。 Execution engine(执行引擎) 执行classes中的指令。 Native Interface(本地接口) 与native libraries交互,是其它编程语言交互的接口。 Runtime data area(运行时数据区域) 这就是我们常说

    2024年03月10日
    浏览(61)
  • JAVA后端开发面试基础知识(八)——Spring

    Spring是一个轻量级Java开发框架 我们一般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发,比如说 Spring 支持 IoC(Inverse of Control:控制反转) 和 AOP(Aspect-Oriented Programming:面向切面编程)、可以很方便地对数据库进行访问、

    2024年03月10日
    浏览(74)
  • html基础面试题 & html的元素居中的常用方法(基础知识温习)

    html基础面试题 html的元素居中的常用方法日常温习 1, 使用 text-align: center; 属性 : 对于内联元素(如文本或图片),可以将其父元素的 text-align 属性设置为 center 。 2, 使用 margin 属性 :对于块级元素,可以通过设置左右边距为 auto 来实现水平居中。这个方法适用于已知宽度

    2024年02月13日
    浏览(44)
  • [C++]C++基础知识概述

      目录 C++基础知识概述::                                    1.什么是C++                                    2.C++发展史                                         3.C++                                    4.命名空间                              

    2023年04月16日
    浏览(52)
  • C++ | 语句的基础知识(夯实基础)

    本篇文章主要介绍数据结构中 C++ 的语句内容,适合有 零基础 的同学,文中描述和代码示例很详细,干货满满,感兴趣的小伙伴快来一起学习吧! ☀️大家好!我是新人博主朦胧的雨梦,希望大家多多关照和支持😝😝😝 🌖大家一起努力,共同成长,相信我们都会遇到更好

    2024年02月05日
    浏览(52)
  • 【C++】C++模板基础知识篇

    个人主页 : zxctscl 文章封面来自:艺术家–贤海林 如有转载请先通知 实现一个通用的交换函数: 在实现不同类型的参数Swap就得写很多个, 用起来太麻烦了。 使用函数重载虽然可以实现,但是有一下几个不好的地方: 重载的函数仅仅是类型不同,代码复用率比较低,只要

    2024年03月28日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包