游戏客户端开发常见八股文
c++篇
前言
看到了一些关于游戏开发c++笔试、面试题,但是有题目没有答案,作为一个只会一点点c++的小菜鸡就记录一下,以下问题答案都是chatGPT回答以及百度答案
面试问题来源:游戏开发岗面试总结
一、C++内存管理常考察点
1. C++的构造函数,复制构造函数,和析构函数
-
构造函数(Constructor):C++中的构造函数用于初始化对象的数据成员。构造函数的名称与类名相同,没有返回类型,包括参数列表和函数体。构造函数在创建对象时自动调用,并且可以重载,即可以有多个构造函数。
-
复制构造函数(Copy Constructor):复制构造函数用于通过已有对象创建一个新对象。复制构造函数的参数是一个同类的对象引用,它用于初始化新对象的数据成员。如果没有显式定义复制构造函数,C++会自动生成一个默认的复制构造函数。复制构造函数通常使用深拷贝(deep copy)来避免浅拷贝(shallow copy)带来的问题。
-
析构函数(Destructor):析构函数用于在对象销毁时释放资源和做一些清理工作。析构函数的名称与类名相同,前面加上一个波浪号(~)作为前缀。析构函数没有返回类型,没有参数。当对象超出作用域、被删除或程序结束时,析构函数会被自动调用。如果没有显式定义析构函数,C++会自动生成一个默认的析构函数。
代码如下(示例):
#include <iostream>
class MyClass {
public:
// 默认构造函数
MyClass() {
std::cout << "Default constructor called" << std::endl;
}
// 带参数的构造函数
MyClass(int value) {
std::cout << "Parameterized constructor called with value: " << value << std::endl;
}
// 复制构造函数
MyClass(const MyClass& other) {
std::cout << "Copy constructor called" << std::endl;
}
// 析构函数
~MyClass() {
std::cout << "Destructor called" << std::endl;
}
};
int main() {
MyClass obj1; // 调用默认构造函数
MyClass obj2(10); // 调用带参数的构造函数
MyClass obj3 = obj1; // 调用复制构造函数
obj3 = obj2; // 调用赋值运算符(非复制构造函数)
return 0;
}
输出结果:
Default constructor called
Parameterized constructor called with value: 10
Copy constructor called
Destructor called
Destructor called
Destructor called
2. 什么是深复制和浅复制
深复制(deep copy)和浅复制(shallow copy)是在编程中常用的两种对象复制方法。
- 浅复制是指创建一个新的对象,新对象的属性值与原对象相同,但对象内部的引用类型数据(如数组、对象等)仍然指向原对象的引用。也就是说,浅复制只是复制了对象的引用,而不是实际的数据。因此,修改新对象的属性值可能会影响到原对象。
- 深复制是指创建一个全新的对象,新对象的属性值和原对象相同,但是对象内部的引用类型数据也会被复制,而不是简单的引用。这意味着,深复制创建的新对象是完全独立于原对象的,对新对象的修改不会影响原对象。
简而言之,浅复制只复制对象的引用,而深复制复制对象的内容。
3.构造函数和析构函数哪个能写成虚函数,为什么
析构函数可以被写成虚函数,而构造函数不能被写成虚函数。
- 虚函数是用于实现多态的概念,能够在运行时根据对象的实际类型来确定调用的方法。而构造函数在对象创建时被调用,此时对象的实际类型还不确定,因此无法使用虚函数。
- 析构函数是用于释放对象占用的资源,包括内存、文件句柄等。在继承关系中,当基类指针指向派生类对象时,如果析构函数不是虚函数,那么在调用delete操作时只会调用基类的析构函数,而不会调用派生类的析构函数。这会导致派生类对象中的资源无法被正确释放,可能会造成内存泄漏等问题。因此,为了确保派生类对象的析构函数能够正确调用,通常会将基类的析构函数声明为虚函数。
总结:由于构造函数在对象创建时被调用,对象的实际类型还不确定,无法使用虚函数;而析构函数在对象销毁时被调用,对象的实际类型已经确定,可以使用虚函数实现多态。
4.C++数组,链表,二叉树的内存排列是什么样的
-
数组的内存排列是连续的,即所有元素在内存中是相邻存储的。
-
链表的内存排列是非连续的,每个节点包含数据和指向下一个节点的指针,节点在内存中可以分布在不同的位置。
-
二叉树的内存排列是通过指针链接的,每个节点包含数据以及指向左右子节点的指针。
5.结构体占多大内存如何计算,类占用多大空间如何计算,空类的空间是多少,为什么
- 结构体的大小是由其成员变量的大小之和决定的,可以使用sizeof运算符来计算结构体的大小。
struct A {
char y; //char类型,1字节
char* z; //指针类型,在 32 位系统上为 4 字节,在 64 位系统上为 8 字节
int x; //int类型,4字节
};
cout<<sizeof(A)<<endl;
因此,整个结构体 A 的大小为 1 + 4 + 4 = 9 字节。但是,由于内存对齐的原因,编译器会将结构体的大小调整为 12 字节,以确保每个成员的地址都能够对齐到合适的内存边界。
这里是假设