c++面试题

1.面向对象的三大特性

  • 封装
    隐藏对象的属性和实现细节,仅对外公开接口和对象进行交互。
    封装实际上是由编译器识别关键字public,private,和protect,体现在类的成员可以有公有成员(public),私有成员(private),保护成员(protect)。
    私有成员是封装体内被隐藏的部分,只有类的成员函数财可以访问私有成员,而在类体外的函数是不能访问的。公有成员是封装体与外界的一个接口,类体外的函数可以访问公有成员;保护成员是只有该类的成员函数和该类的派生类才可以访问的。
    封装的作用是:降低耦合

  • 继承
    继承是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类;

//Person为父类,基类,Student为派生类,子类,public为继承方式
class Student:public Person
{
}

在这里插入图片描述
在这里插入图片描述

  • 继承中的作用域
    在继承体系中基类和派生类都有独立的作用域,
    子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。
    实际中在继承体系里面最好不要定义同名的成员。

  • 菱形继承

多继承的一种特殊情况,会有数据冗余和二义性。

  • 多态

2.类的访问权限:private,protected,public

https://blog.csdn.net/qq_31542849/article/details/104512345

3. 类的构造函数、析构函数、赋值函数、拷贝函数

拷贝构造函数参数必须传引用而不是传值,传只会造成递归构造.

https://www.cnblogs.com/alantu2018/p/8459250.html

4.移动构造函数和拷贝构造函数的区别

首先讲讲拷贝构造函数:拷贝构造函数是先将传入的参数对象进行一次深拷贝,再传给新对象。这就会有一次拷贝对象的开销,并且进行了深拷贝,就需要给对象分配地址空间。而移动构造函数就是为了解决这个拷贝开销而产生的。移动构造函数首先将传递参数的内存地址空间接管,然后将内部所有指针设置为nullptr,并且在原地址上进行新对象的构造,最后调用原对象的的析构函数,这样做既不会产生额外的拷贝开销,也不会给新对象分配内存空间。

5.深拷贝与浅拷贝的区别

6. 空类有哪些函数?空类的大小

https://blog.csdn.net/qq_41842669/article/details/116230272

https://blog.csdn.net/jane_yao/article/details/79644838?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.base

7. 内存分区:全局区、堆区、栈区、常量区、代码区

https://blog.csdn.net/william_cw/article/details/96429114

c++内存分配方式

https://blog.csdn.net/liuweiyuxiang/article/details/90453562

8. c++与c的区别和联系

  • c++与c的联系:
    c++是在c语言的基础上开发的一种面向对象编程语言,支持多种编程范式–面向对象编程,泛型编程和过程化编程。c++在c的基础上增添类,c是结构化语言,考虑如何通过一个过程,对输入进行运算得到输出,而对于c++,考虑的是如何构造一个对象模型。
  • c++与c的区别:
  1. c是面向过程的语言,而c++是面向对象的语言
  2. C和C++动态管理内存的方法不一样,C是使用malloc、free函数,而C++不仅有malloc/free,还有new/delete关键字。那malloc/free和new/delete差别?
    malloc/free和new/delete差别:

①、malloc/free是C和C++语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。

②、由于malloc/free是库函数不是运算符,不在编译器范围之内,不能够把执行构造函数和析构函数的任务强加入malloc/free。因此C++需要一个能完成动态内存分配和初始化工作的运算符new,一个能完成清理与释放内存工作的运算符delete。

③、new可以认为是malloc加构造函数的执行。new出来的指针是直接带类型信息的。而malloc返回的都是void指针。

④、malloc是从堆上开辟空间,而new是从自由存储区开辟(自由存储区是从C++抽象出来的概念,不仅可以是堆,还可以是静态存储区)。

⑤、malloc对开辟的空间大小有严格指定,而new只需要对象名。

⑥、malloc开辟的内存如果太小,想要换一块大一点的,可以调用relloc实现,但是new没有直观的方法来改变。

  1. c++的类是c没有的,c中的struct可以在c++中等同类来使用,struct和类的差别是,struct的成员默认访问修饰符是public,而类默认是private。

  2. c++支持重载,而c不支持重载。

  3. c++中有引用,而c没有。那指针和引用有什么差别?

指针和引用的区别:

①、指针有自己的一块空间,而引用只是一个别名。

②、使用sizeof查看一个指针大小为4(32位),而引用的大小是被引用对象的大小。

③、指针可以是NULL,而引用必须被初始化且必须是对一个以初始化对象的引用。

④、作为参数传递时,指针需要被解引用才可以对对象进行操作,而直接对引用的修改都会改变引用所指向的对象。

⑤、指针在使用中可以指向其它对象,但是引用只能是一个对象的引用,不能被修改。

⑥、指针可以有多级指针(**p),而引用只有一级。

⑦、指针和引用使用++运算符的意义不一样。

  1. c++全部变量的默认连接属性是外连接,而c是内连接。
  2. c中用const修饰的变量不可以用在定义数组时的大小,但是c++用const修饰的变量可以。
  3. c++有很多特有的输入输出流。

9. struct和class的区别

struct更适合看成是一个数据结构的实现体,class更适合看成是一个对象的实现体。

struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的。

10. struct内存对齐

一种提高内存访问速度的策略,cpu在访问未对齐的内存西药经过两次内存访问,而经过内存对齐一次就可以了。

  1. 结构体的内存布局记住两个规则即可,这是编译器的优化措施。
    规则1:结构体中第一个成员的偏移量是0,以后每个成员的位置是x的倍数;
    x = min(#pragma pack(), 该成员自身的长度)
    规则2:成员对齐后,结构体自身也要对齐,按照y的倍数进行;
    y = min(#pragma pack(), 最大成员尺寸)。
    其中#pragma pack() 代表编译器默认以多少字节进行对齐,通常情况下是8B。

11.new/delete与malloc/free的区别与联系

  1. malloc/free为C的标准库函数,函数原型为:
void* malloc(size_t size)//参数代表字节个数

void free(void* pointer)//参数代表内存地址

new、delete则为C++的操作运算符,它调用的分别为赋值运算符重载operator new()和operator delete();

  1. 在使用上,malloc/free如下:
void func()
 
{
 
//开辟一个空间
 
int* p1=(int*)malloc(sizeof(int));
 
if(p1==NULL)
 
{
 
exit(1);
 
}
 
free(p1);
 
//开辟多个空间
 
int*p2=(int*)malloc(sizeof(int)*4);
 
if(p2==NULL)
 
{
 
exit(1);
 
}
 
free(p2);
 
}

用malloc分别开辟了1个和4个整型大小的空间和并free释放它们;

new/delete如下:

void func()
 
{
 
//开辟一个空间
 
int* p1=new int(1);
 
delete p1;
 
//开辟多个空间
 
int*p2=new int[4];
 
delete []p2;
 
}

由此可知:

(1)malloc开辟空间类型大小需手动计算,new是由编译器自己计算;

(2)malloc返回类型为void*,必须强制类型转换对应类型指针,new则直接返回对应类型指针;

(3)malloc开辟内存时返回内存地址要检查判空,因为若它可能开辟失败会返回NULL;new则不用判断,因为内存分配失败时,它会抛出异常bac_alloc,可以使用异常机制;

(4)无论释放几个空间大小,free只传递指针,多个对象时delete需加[](原因在第3);

  1. malloc/free为函数只是开辟空间并释放,new/delete则不仅会开辟空间,并调用构造函数和析构函数进行初始化和清理,如下为new/delete、new[]/delete[]实现机制:

而new[]/delete[]则为:
new/delete与malloc/free的区别与联系详解!

12.内存泄漏的情况

C++中内存泄漏的几种情况

13.sizeof与strlen对比

sizeof与strlen对比总结

14.指针和引用的区别

C++中指针与引用的区别

15.野指针产生与避免

c++野指针的原理以及避免方法

16.多态:动态多态、静态多态

c++中的静态多态和动态多态

17.虚函数实现动态多态的原理、虚函数与纯虚函数的区别

C++ 多态的实现及原理(虚函数与纯虚函数)

18. 继承时,父类的析构函数是否为虚函数?构造函数能不能为虚函数?为什么?

C++中构造函数和析构函数是否可以是虚函数,为什么?

19.静态多态:重写、重载、模板

c++多态,虚函数,重载函数,模板函数

20.static关键字:修饰局部变量、全局变量、类中成员变量、类中成员函数

C++中类的(static)静态成员变量与(static)静态成员函数

static作用(修饰函数、局部变量、全局变量、成员函数、成员变量)

  1. 全局静态变量
    在全局变量前加上关键字static,全局变量就定义成一个全局静态变量.

静态存储区,在整个程序运行期间一直存在。

初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);

作用域:全局静态变量在声明他的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。

  1. 局部静态变量

在局部变量之前加上关键字static,局部变量就成为一个局部静态变量。

内存中的位置:静态存储区

初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化);

作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变;

  1. 静态函数

在函数返回类型前加static,函数就定义为静态函数。函数的定义和声明在默认情况下都是extern的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。

函数的实现使用static修饰,那么这个函数只可在本cpp内使用,不会同其他cpp中的同名函数引起冲突;

warning:不要再头文件中声明static的全局函数,不要在cpp内声明非static的全局函数,如果你要在多个cpp中复用该函数,就把它的声明提到头文件里去,否则cpp内部声明需加上static修饰;

  1. 类的静态成员

在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。对多个对象来说,静态数据成员只存储一处,供所有对象共用

  1. 类的静态函数

静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。

在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员(这点非常重要)。如果静态成员函数中要引用非静态成员时,可通过对象来引用。从中可看出,调用静态成员函数使用如下格式:<类名>::<静态成员函数名>(<参数表>);

21. const关键字:修饰变量、指针、类对象、类中成员函数

C++ const关键字的总结(全局/局部变量、修饰指针和引用、成员函数和数据成员、修饰类对象、const与宏定义的区别、Static与Const的区别)

https://leetcode-cn.com/leetbook/read/cpp-interview-highlights/efo7m6/

22. c++重载,覆盖,重写的区别

https://blog.csdn.net/nwpubear/article/details/81452618

20、static关键字:修饰局部变量、全局变量、类中成员变量、类中成员函数

https://www.cnblogs.com/dc10101/archive/2007/08/22/865556.html

https://blog.csdn.net/qq_14898543/article/details/50195263?utm_source=blogxgwz9

21、const关键字:修饰变量、指针、类对象、类中成员函数

https://blog.csdn.net/JMW1407/article/details/108443185

22、extern关键字:修饰全局变量

https://blog.csdn.net/puppet_master/article/details/47681849

23、volatile关键字:避免编译器指令优化

https://blog.csdn.net/lk07828/article/details/42496251

24、四种类型转换:static_cast、dynamic_cast、const_cast、reinterpret_cast

https://www.cnblogs.com/chenyangchun/p/6795923.html

C++中四种类型转换是:static_cast, dynamic_cast, const_cast, reinterpret_cast
1、const_cast

用于将const变量转为非const

2、static_cast

用于各种隐式转换,比如非const转const,void*转指针等, static_cast能用于多态向上转化,如果向下转能成功但是不安全,结果未知;

3、dynamic_cast

用于动态类型转换。只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用。向下转化时,如果是非法的对于指针返回NULL,对于引用抛异常。要深入了解内部转换的原理。

向上转换:指的是子类向基类的转换

向下转换:指的是基类向子类的转换

它通过判断在执行到该语句的时候变量的运行时类型和要转换的类型是否相同来判断是否能够进行向下转换。

4、reinterpret_cast

几乎什么都可以转,比如将int转指针,可能会出问题,尽量少用;

5、为什么不使用C的强制转换?

C的强制转换表面上看起来功能强大什么都能转,但是转化不够明确,不能进行错误检查,容易出错

25、右值引用

26、std::move函数

27、四种智能指针及底层实现:auto_ptr、unique_ptr、shared_ptr、weak_ptr

https://www.pianshen.com/article/5143232295/

C++里面的四个智能指针: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是c++11支持,并且第一个已经被11弃用。
为什么要使用智能指针:

智能指针的作用是管理一个指针,因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。

  1. auto_ptr(c++98的方案,cpp11已经抛弃)

采用所有权模式。

auto_ptr< string> p1 (new string ("I reigned lonely as a cloud.”));
auto_ptr p2;
p2 = p1; //auto_ptr不会报错.
此时不会报错,p2剥夺了p1的所有权,但是当程序运行时访问p1将会报错。所以auto_ptr的缺点是:存在潜在的内存崩溃问题!

  1. unique_ptr(替换auto_ptr)

unique_ptr实现独占式拥有或严格拥有概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露(例如“以new创建对象后因为发生异常而忘记调用delete”)特别有用。

采用所有权模式,还是上面那个例子

unique_ptr p3 (new string (“auto”)); //#4
unique_ptr p4; //#5
p4 = p3;//此时会报错!!

编译器认为p4=p3非法,避免了p3不再指向有效数据的问题。因此,unique_ptr比auto_ptr更安全。

另外unique_ptr还有更聪明的地方:当程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做,比如:

unique_ptr pu1(new string (“hello world”));
unique_ptr pu2;
pu2 = pu1; // #1 not allowed
unique_ptr pu3;
pu3 = unique_ptr(new string (“You”)); // #2 allowed

其中#1留下悬挂的unique_ptr(pu1),这可能导致危害。而#2不会留下悬挂的unique_ptr,因为它调用 unique_ptr 的构造函数,该构造函数创建的临时对象在其所有权让给 pu3 后就会被销毁。这种随情况而已的行为表明,unique_ptr 优于允许两种赋值的auto_ptr 。

注:如果确实想执行类似与#1的操作,要安全的重用这种指针,可给它赋新值。C++有一个标准库函数std::move(),让你能够将一个unique_ptr赋给另一个。例如:

unique_ptr ps1, ps2;
ps1 = demo(“hello”);
ps2 = move(ps1);
ps1 = demo(“alexia”);
cout << *ps2 << *ps1 << endl;

  1. shared_ptr

shared_ptr实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时候释放。从名字share就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。

shared_ptr 是为了解决 auto_ptr 在对象所有权上的局限性(auto_ptr 是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针。

成员函数:

use_count 返回引用计数的个数

unique 返回是否是独占所有权( use_count 为 1)

swap 交换两个 shared_ptr 对象(即交换所拥有的对象)

reset 放弃内部对象的所有权或拥有对象的变更, 会引起原有对象的引用计数的减少

get 返回内部对象(指针), 由于已经重载了()方法, 因此和直接使用对象是一样的.如 shared_ptr sp(new int(1)); sp 与 sp.get()是等价的

  1. weak_ptr

weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

class B;
class A
{
public:
shared_ptr pb_;
~A()
{
cout<<“A delete\n”;
}
};
class B
{
public:
shared_ptr pa_;
~B()
{
cout<<“B delete\n”;
}
};
void fun()
{
shared_ptr pb(new B());
shared_ptr
pa(new A());
pb->pa_ = pa;
pa->pb_ = pb;
cout<<pb.use_count()<<endl;
cout<<pa.use_count()<<endl;
}
int main()
{
fun();
return 0;
}
可以看到fun函数中pa ,pb之间互相引用,两个资源的引用计数为2,当要跳出函数时,智能指针pa,pb析构时两个资源引用计数会减一,但是两者引用计数还是为1,导致跳出函数时资源没有被释放(A B的析构函数没有被调用),如果把其中一个改为weak_ptr就可以了,我们把类A里面的shared_ptr pb_; 改为weak_ptr pb_; 运行结果如下,这样的话,资源B的引用开始就只有1,当pb析构时,B的计数变为0,B得到释放,B释放的同时也会使A的计数减一,同时pa析构时使A的计数减一,那么A的计数为0,A得到释放。

注意的是我们不能通过weak_ptr直接访问对象的方法,比如B对象中有一个方法print(),我们不能这样访问,pa->pb_->print(); 英文pb_是一个weak_ptr,应该先把它转化为shared_ptr,如:shared_ptr p = pa->pb_.lock(); p->print();

28.shared_ptr中的循环引用怎么解决?(weak_ptr)

https://blog.csdn.net/Jacketinsysu/article/details/53343534

29.vector与list比较

https://blog.csdn.net/qq_42659468/article/details/99684322

30.vector迭代器失效的情况

https://blog.csdn.net/AXuan_K/article/details/52588441

31.map与unordered_map对比

添加链接描述

32.set与unordered_set对比

https://www.cnblogs.com/Jawen/p/10821702.html

33.STL容器空间配置器

https://blog.csdn.net/qq_40421919/article/details/89192383

限制类只能在堆上

https://blog.csdn.net/u010536615/article/details/45337841

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值