序言:山下旌旗在望,山头鼓角相闻。 敌军围困万千重,我自岿然不动。 早已森严壁垒,更加众志成城。 黄洋界上炮声隆,报道敌军宵遁。《西江月·井冈山》
【看山是山】
【看山不是山】
1. nullptr与NULL有什么区别?
nullptr是C++11引入的类型安全的空指针常量,类型是std::nullptr_t。NULL通常被定义为0或((void*)0),在某些情况下可能引起类型混淆。
2. volatile、virtual和explicit?
volatile、virtual和explicit是三个非常重要的关键字,它们用于不同的目的,影响变量的存储、类的继承和构造函数的使用。
volatile:
作用:volatile关键字用于指示编译器不要对标记的变量进行优化,提示该变量可能会在程序外部被修改,如硬件寄存器或中断服务例程。
用法:volatile通常用于多线程编程、嵌入式系统编程和信号处理,以确保变量的值总是从内存中读取,而不是使用寄存器中的缓存值。
virtual
作用: virtual关键字用于声明虚函数,使得派生类可以覆盖基类中的函数,实现多态。它允许在运行时决定调用哪一个类的成员函数。
用法: 在基类中使用virtual声明虚函数,派生类可以通过覆盖该虚函数实现多态行为。
explicit
作用: explicit关键字用于防止构造函数进行隐式转换或复制初始化。它强制要求使用显式的类型转换,从而避免不必要的或意外的类型转换。
用法: 通常用于单参数构造函数,以防止编译器自动进行类型转换。
3. 多讲一些关于virtual的东西:CPP多态有哪些实现?
多态:允许一个接口以不同方式实现。
(1) 静态多态
原理:编译器在编译时根据函数签名(函数名、参数类型、入参数量)决定具体调用哪个函数。
a.重载(在编译时期就可以通过函数名和参数确定需要调用那个函数);
b.模板
(2) 动态多态
原理:依赖于虚函数表、虚函数指针。当一个类中有虚函数时,编译器为该类创建一个虚函数表,表中储存了该类的虚函数地址。每个对象都有一个虚函数指针(vptr),指向所属类的虚函数表。在程序运行是通过vptr找到对应的虚函数表,并通过表中保存的地址调用相应的函数。
a.虚函数(通过运行阶段才能知道需要调用那个对象); b.纯虚函数; c.虚析构函数; d.虚函数表
4. new/delete是函数吗?它们和malloc/free有何区别?
他们都是用于动态申请内存和释放内存的组合。
- new操作符从自由存储区上为对象动态分配内存空间,而malloc从堆区上。
- 使用new创建对象在内存分配时会自动调用构造函数,同时对对象完成动态内存分配和初始化工作,delete也能自动调用析构函数,对对象完成清理与释放内存。而malloc/free不能。
- new能够自动分配空间大小,new返回的是指定类型的指针,而malloc返回的是 void *类型,需要强制类型转换,并且要指定申请内存大小。
- new/delete是C++中的运算符,在编译器控制权限之内,malloc/free是C标准库中提供的函数。
5. const和define和inline
- 作用的阶段:#define是在编译的预处理阶段起作用,而const是在编译、运行的时候起作用。inline内联函数在编译阶段进行替换。
- 作用的方式:#define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误。
- 存储方式:#define只是进行展开,有多少地方使用,就替换多少次,它定义的宏常量在内存中有若干个备份;const定义的只读变量在程序运行过程中只有一份备份。
- 空间占用:const 常量占用数据段空间。#define 预处理后占用代码段空间。
- 编译器优化:
inline
函数可以被编译器优化,而宏定义和const
常量则不能。
#define:
#define
是预处理器指令,它在编译之前进行处理。- 它将所有出现的宏定义替换为宏定义的值。
- 没有类型安全检查,因为它只是简单的文本替换。
- 宏定义不能在条件编译中使用。
- 宏定义不能在函数内部定义。
const
const
关键字定义的是常量,它在编译时进行处理。const
定义的常量具有数据类型,并且可以进行类型安全检查。const
定义的常量可以在条件编译中使用。const
定义的常量可以在函数内部定义。
inline
inline
关键字用于定义内联函数,它在编译时进行处理。- 内联函数是一种小的、简单的函数,编译器会尝试将函数调用替换为函数体,以减少函数调用的开销。
- 内联函数通常用于定义简单的函数,如数学计算函数。
- 内联函数可以定义在头文件中,并且可以被多个源文件包含。
使用建议:
- 对于需要类型安全检查的常量,使用
const
。 - 对于简单的、没有类型的常量定义,可以使用
#define
。 - 对于希望减少调用开销的小函数,使用
inline
。
6. extern和static
extern:
extern
关键字用于声明一个全局变量或函数,它告诉编译器该变量或函数的定义在其他地方。extern
变量具有外部链接属性,这意味着它可以被其他文件访问,只要它们通过extern
关键字声明。- 如果没有使用
extern
关键字声明全局变量,那么默认情况下它也具有外部链接属性。 extern
用于在多个文件之间共享全局变量或函数。
static:
static
关键字用于定义静态变量或静态函数。- 静态变量具有内部链接属性,这意味着它们只能在定义它们的文件中被访问。
- 静态变量的存储期是整个程序的执行期间,它们在程序启动时初始化,并在程序结束时销毁。
- 静态函数也具有内部链接属性,只能在定义它们的文件中被调用。
- 在函数内部使用
static
关键字定义的局部变量具有静态存储期,这意味着它们的值在函数调用之间是持久的。
Q1:static修饰全局变量、局部变量和成员变量的区别
Q2:如何引用一个已经定义过的全局变量
(1) 可以引用头文件的方式。假设此变量写错了,那么会在编译期间报错;
(2) extern关键字。假设此变量写错了,那么会在链接期间报错。
Q3:static一个全局变量后再使用extern,在另一个文件中调用时会发生什么?
不会被访问到。static关键字用于限制函数的链接范围。当你在一个源文件中使用static修饰函数时,函数的作用域仅限于这个文件本身,他不能被其他原文件访问到,无论你是否将该函数声明在头文件中还是再使用extern关键字。#include
引用头文件不控制链接属性。
全局变量(无
static
,有extern
):
- 存储在静态存储区。
- 可以被程序中的任何函数访问。
静态全局变量(有
static
):
- 存储在静态存储区。
- 只能在定义它们的文件中访问。
局部变量(无
static
,自动存储期):
- 存储在栈上。
- 只能在定义它们的函数中访问。
静态局部变量(有
static
):
- 存储在静态存储区。
- 只能在定义它们的函数中访问,但它们的值在函数调用之间是持久的。
寄存器变量(有
register
):
- 建议存储在寄存器中,但实际存储位置由编译器决定。
- 只能在定义它们的函数中访问。
线程局部变量(有
_Thread_local
):
- 存储在线程的栈上。
- 每个线程都有自己的独立副本。
7. class和struct和union
(1) 访问控制:struct
成员默认是公开的;class
成员默认是私有的。
(2) 成员函数:struct
不能包含函数;class
可以包含函数。
(3) 面向对象编程:struct
不支持面向对象编程的特性;class
支持封装、继承和多态。
内存对齐
- Class:类的成员通常会有对齐填充,以满足最宽成员的对齐要求。类的总大小通常是最大成员对齐要求的倍数。
- Struct:与类类似,结构体也会有对齐填充,但默认成员是公有的。
- Union:联合体的内存布局与类和结构体不同,因为它的成员共享内存空间。因此,联合体的大小等于最大成员的大小,而不是所有成员大小的总和。
例如:
#include <iostream> class MyClass { public: int a; double b; char c; }; struct MyStruct { int a; double b; char c; }; union MyUnion { int a; double b; char c; }; int main() { std::cout << "Size of MyClass: " << sizeof(MyClass) << std::endl; std::cout << "Size of MyStruct: " << sizeof(MyStruct) << std::endl; std::cout << "Size of MyUnion: " << sizeof(MyUnion) << std::endl; return 0; }
在64的系统上,24/24/8
8. friend关键字
用于指定友元类或友元函数,使其可以访问类的私有成员和保护成员。
9. final关键字
当前我这个类就是最终类,我不想让别的类再继承我自己
【看山还是山】
滴答~