欢迎关注本公众号,专注面试题拆解
分享一套视频课程:《C++实现百万并发服务器》 面试需要项目的可以找我获取,免费分享。 欢迎V:fb964919126
new出来的对象可以使用bzero等函数初始化内部变量为0吗?
首先,这里得说明下,bzero是一个特定于某些库(如BSD库)的函数,用于清空一块内存。在现代的C和C++标准库中,并没有bzero函数的标准定义。memset是C标准库中的函数,并且也是C++标准库的一部分,用于填充或清零内存块。
问题转化为:new出来的对象是否可以使用memset初始化内部变量为0?
能不能用其实要看具体的对象是什么样的,简单来说如果对象不含有指针,那么可以使用,如果对象含有了指针,那么就不能使用。
何时可以使用 memset?
基本类型和固定大小的数组:
如果对象包含基本类型(如 int, char 等)或固定大小的数组,可以直接使用 memset 来初始化这些成员变量。
这种情况下,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 只是清零内存,不会调用成员变量的构造函数或执行其他必要的初始化逻辑。
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 或其他值,从而破坏虚函数表的指针,导致虚函数调用失败。
未定义行为:
直接清零可能会导致对象处于未定义状态,使得后续的操作变得不可预测。
假设有一个包含虚函数的类
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电子书资料赠送
精彩文章合集
专题推荐