面试题:new出来的对象可以使用bzero等函数初始化内部变量为0吗?——信锐技术一面

旅行   2024-09-26 12:04   广东  

欢迎关注本公众号,专注面试题拆解

分享一套视频课程:《C++实现百万并发服务器》 面试需要项目的可以找我获取,免费分享。 欢迎V:fb964919126


new出来的对象可以使用bzero等函数初始化内部变量为0吗?

首先,这里得说明下,bzero是一个特定于某些库(如BSD库)的函数,用于清空一块内存。在现代的C和C++标准库中,并没有bzero函数的标准定义。memset是C标准库中的函数,并且也是C++标准库的一部分,用于填充或清零内存块。

问题转化为:new出来的对象是否可以使用memset初始化内部变量为0?

能不能用其实要看具体的对象是什么样的,简单来说如果对象不含有指针,那么可以使用,如果对象含有了指针,那么就不能使用。

何时可以使用 memset?

基本类型和固定大小的数组:

如果对象包含基本类型(如 int, char 等)或固定大小的数组,可以直接使用 memset 来初始化这些成员变量。

这种情况下,memset 只是将内存区域清零,不会影响对象的内部状态。

#include <iostream>#include <cstring> // 包含 memset
struct Data { int arr[10]; char str[50];};
int main() { Data* data = new Data();
// 使用 memset 清零数组成员 memset(data, 0, sizeof(Data));
// 确认初始化 for (int i = 0; i < 10; ++i) { std::cout << data->arr[i] << " "; // 输出 0 0 0 ... 0 } std::cout << std::endl;
std::cout << data->str << std::endl; // 输出空字符串
delete data; // 释放内存
return 0;}


何时不能使用 memset?

包含指针的复杂对象:

如果对象包含指针或其他需要动态分配内存的成员变量,直接使用 memset 来清零这些成员可能会导致未定义行为。

这是因为 memset 只是清零内存,不会调用成员变量的构造函数或执行其他必要的初始化逻辑。

#include <iostream>#include <cstring> // 包含 memset
struct ComplexData { char* p;};
int main() {
try { ComplexData* data = new ComplexData(); data->p = new char[50]; memset(data, 0, sizeof(ComplexData)); strcpy(data->p, "Hello, World!"); std::cout << data->p << std::endl; delete[] data->p; // 释放动态分配的内存 delete data; // 释放对象内存 } catch (std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; }
return 0;}


工做当中碰到的ERROR场景:

曾经工作当中碰到过类似引起的bug,当时定位了一周才定位到问题,最后发现原因是有个同事定义结构体,结构体里面有变量是std::string类型的,但是定义对象的时候,习惯性的用了memset。这里使用memset破坏了std::string对象的内部机制。string内部实现里面也用到了指针。

当结构体或类的本身或其基类中存在虚函数时,需要谨慎使用memset。

当结构体或类中存在虚函数时,需要谨慎使用 memset。这是因为存在虚函数的类会有虚函数指针(vptr),而直接使用 memset 来清零整个对象的内存可能会破坏这个指针,从而导致未定义行为。

当一个类中定义了至少一个虚函数时,编译器会为该类生成一个虚函数指针(vptr),指向虚函数表(vtable)。这个虚函数表包含该类中所有虚函数的地址以及一些其他的元数据。vptr 是类对象的一部分,通常位于对象的最前面。

使用 memset 清零包含虚函数的类对象的影响:

当使用 memset 清零包含虚函数的类对象时,可能会导致以下问题:

破坏虚函数指针(vptr):
使用 memset 清零对象内存会将 vptr 设置为 0 或其他值,从而破坏虚函数表的指针,导致虚函数调用失败。
未定义行为:
直接清零可能会导致对象处于未定义状态,使得后续的操作变得不可预测。

假设有一个包含虚函数的类

#include <iostream>#include <cstring> // 包含 memset
class Base {public: virtual void print() const { std::cout << "Base" << std::endl; } virtual ~Base() {}};
class Derived : public Base {public: void print() const override { std::cout << "Derived" << std::endl; }};
int main() { Derived derived; Base* basePtr = &derived;
// 使用 memset 清零对象 memset(&derived, 0, sizeof(Derived));
// 尝试调用虚函数 basePtr->print(); // 可能导致未定义行为
return 0;}

在这个例子中,memset用于清零Derived类对象。由于Derived继承自 Base 并重写了虚函数 print(),直接使用memset清零会破坏虚函数指针(vptr),导致虚函数调用失败。

总结:

对于只包含基本类型或固定大小数组的对象,可以直接使用 memset 来初始化。

对于包含指针或其他需要动态分配内存的对象,应该使用构造函数和初始化列表来确保正确的初始化。

直接使用 memset 清零包含指针的对象可能会导致未定义行为,因此应谨慎使用。

这里给出一个思考题目:

普通没有虚函数的类,我们都知道普通类对象会隐含一个this指针,那么是否可以使用memset呢?

end



CppPlayer 



关注,回复【电子书】珍藏CPP电子书资料赠送

精彩文章合集

专题推荐

【专辑】计算机网络真题拆解
【专辑】大厂最新真题
【专辑】C/C++面试真题拆解

CppPlayer
一个专注面试题拆解的公众号
 最新文章