每日面经(二十一)

1.extern C 的作用

在 C++ 中,当我们声明一个函数时,编译器会将该函数名进行名称重整,以便让其能够和其它同名函数进行区分。但是,在某些情况下,我们需要禁用这种默认的名称重整,而是使用C语言中以一种特定的方式命名函数,这时就需要使用 extern "C"。

具体来说,extern "C" 用于告诉编译器将函数名以C语言的方式进行处理,也就是不进行名称重整。这对于编写链接到动态链接库(DLL)或静态链接库(LIB)的程序非常有用,因为库文件中的函数名通常不进行名称重整。如果在编译库文件时使用了 extern "C",则可以确保库中的函数名与头文件中的函数名匹配,从而避免链接错误。

另外需要注意的是,extern "C" 只能用于 C++ 代码中,因为它是 C++ 的一个扩展。在C代码中,函数名不会进行名称重整,因此也不存在这个问题。

2.析构函数没有声明为虚函数一定会发生内存泄露吗?

不一定会发生内存泄露,但是如果析构函数不被声明为虚函数,并且我们使用父类指针指向子类对象并删除该指针时,就会出现问题。

在这种情况下,当我们通过父类指针销毁一个子类对象时,由于该指针只知道它指向父类对象,因此它只会调用父类的析构函数,而不会调用子类的析构函数。这样就会导致子类对象中分配的内存没有被正确地释放,从而发生内存泄漏。

为了避免这个问题,我们需要将父类的析构函数声明为虚函数。这样,当我们通过父类指针删除子类对象时,程序就能够根据实际对象类型来调用正确的析构函数,从而确保释放对象中的所有资源,避免内存泄漏的发生。

3.使用vector需要注意一些什么?

在使用Vector时,需要注意以下几点:

1.插入和删除元素会导致内存重新分配,因此需要尽可能地减少这些操作。如果需要频繁地插入和删除元素,可以考虑使用其他数据结构,比如链表。

2.当Vector中存储的对象是指针类型时,需要手动释放指针指向的内存空间,并在容器释放时确保没有内存泄漏的情况发生。

3.当Vector中存储的对象是自定义类型时,需要实现拷贝构造函数和析构函数,确保对象的正确创建和销毁。并且需要留心浅拷贝和深拷贝的问题。

4.当Vector中存储的对象占用的内存较大时,需要考虑使用智能指针来管理对象的内存。这样可以避免因为忘记释放内存而导致的内存泄漏或野指针问题。

5.在遍历Vector时,需要注意不要越界访问。可以使用迭代器来遍历容器,并确保迭代器指向的元素合法。

6.需要注意使用reserve()方法进行预分配内存,可以提升Vector的性能。

总之,在使用Vector时,需要了解其底层实现和特点,并根据具体的业务场景和需求来进行选择和调整,从而达到最优的效果。

4.c++内存分布

C++程序运行时内存分为以下四个区域,每个区域有其特点和用途:

  1. 代码区(Text Segment):保存程序的指令(机器码),属于只读区,无法修改。

  2. 全局/静态区(Data Segment):存放全局变量和静态变量,如全局变量、静态变量、常量等,程序运行时这个区域的数据会一直存在,在程序运行结束前都不会被释放。

  3. 堆区(Heap):程序员通过new、malloc等动态分配内存方式获取的内存称为堆内存。堆内存的大小并不是固定的,程序运行后,操作系统会分配一块较为连续的内存给堆,而堆内存的销毁是程序员主动释放,否则将造成内存泄露。

  4. 栈区(Stack):存放函数参数值、局部变量等,栈内存的分配与释放自动进行,不需要手动释放,由编译器自动管理。栈区是一块连续的内存区域,大小受制于操作系统的内存大小,动态增长。由于栈内存是受限空间,程序员在使用时需要注意局部变量的作用范围,避免栈溢出问题。

5.派生类是如何寻找虚函数表的。

当一个类中有虚函数时,编译器会为这个类创建一个虚函数表,虚函数表中存储了该类所有虚函数的地址。派生类继承了基类的虚函数表,并可能在其中添加或覆盖一些虚函数。当派生类对象调用虚函数时,会先去虚函数表中查找对应的函数地址,然后再调用对应的函数。

在派生类中调用虚函数时,编译器会根据指针类型和虚函数表进行动态绑定,找到正确的函数实现。如果派生类中重写了父类的虚函数,编译器访问派生类中的虚函数表中的函数地址,而不是父类的虚函数表中的函数地址。

因此,当派生类对象调用虚函数时,它会先访问自己的虚函数表,如果找不到对应的函数,则会去父类的虚函数表中查找。这样,派生类就可以直接调用基类的虚函数,或者在自己的虚函数表中添加或覆盖虚函数。

6.派生类开辟的对象如何跟虚函数表绑定。

派生类对象的虚函数表是继承自基类对象的虚函数表,并且派生类可以在其虚函数表中添加或覆盖虚函数。

当派生类对象创建时,编译器会在对象内存布局中分配一块虚函数表的内存空间,并将基类的虚函数表复制到派生类对象的内存布局中的虚函数表的起始地址处。然后,编译器会更新派生类虚函数表的条目,以匹配派生类中的虚函数,即在虚函数表中指定派生类中的虚函数的地址。

当派生类对象调用虚函数时,编译器会根据派生类对象的类型去访问该派生类对象的虚函数表,并通过虚函数表中的条目跳转到对应的虚函数实现。

因此,派生类的虚函数表和对象是动态绑定的,这意味着当我们使用派生类对象调用虚函数时,编译器会根据对象的运行时类型动态地查找对应的虚函数表,然后通过该虚函数表跳转到正确的虚函数实现。

7.虚函数表在内存上需要存储多少份

每个包含虚函数的类都会有一个对应的虚函数表,因此,如果一个程序中有多个包含虚函数的类,则会有多个虚函数表。

每个有虚函数的对象也都会有一个指向其所属类虚函数表的指针,这个指针称为虚函数表指针,因此,程序中的每个有虚函数的对象都会有一个虚函数表指针。

由于派生类继承了基类的虚函数表,并且可能在其中添加或覆盖一些虚函数,因此程序中每个派生类也都会有一个对应的虚函数表,即使派生类中没有任何新增的虚函数。

因此,一个程序中可能会有大量的虚函数表和虚函数表指针,每一个有虚函数的对象都需要存储一个虚函数表指针。虚函数表和虚函数表指针所占的内存大小取决于编译器实现的具体规则,但通常每个虚函数表指针占据的空间大小是4字节或8字节,而每个虚函数表的大小则取决于类的虚函数数量。

8.C++的内存管理机制是什么,跟Java内存管理机制之间的区别

在C++中,内存管理是由程序员自己负责的,通过调用new和delete或者malloc和free等函数进行手动管理。这种手动管理的好处是能够精确控制内存的分配和释放,缺点则是如果管理不当,很容易出现内存泄漏或者内存溢出等问题。

而在Java中,内存管理是由Java虚拟机(JVM)自动进行的,程序员只需要申请对象,JVM就会在需要的时候分配和释放内存。这种自动管理的好处是可以减少程序员的工作量和出错率,同时也能够保证内存安全和防止内存泄漏等问题。但是,由于JVM需要进行垃圾回收和内存整理等操作,需要消耗一定的系统资源,可能会影响一些高性能场景下的应用。

9..C++中为什么malloc要指定大小,free不需要。

在C++中,调用malloc函数可以在堆上动态分配一段指定大小的内存,并返回一个指向该内存区域的指针。而调用free函数则可以释放该内存区域,让该内存可以被其他程序使用。

之所以调用malloc需要指定内存的大小,是因为malloc函数需要知道要动态分配多少字节的内存,以便为其分配足够的内存空间。而调用free时却不需要指定内存的大小,是因为系统在分配内存时已经记住该内存区域的大小,可以根据该大小来释放内存。因此,free函数只需要传递一个指向待释放内存区域的指针即可,而不需要提供内存区域的大小。

你可能感兴趣的:(面经,c++,windows,开发语言,面试,网络协议)