企业网站建设与管理,手机网站设置方法,wordpress框架是什么,网站是别人做的域名自己怎么续费八股文
1.类和结构体的区别 在 C 中#xff0c;类#xff08;class#xff09;和结构体#xff08;struct#xff09;在语法上几乎是相同的#xff0c;唯一的区别是默认的访问权限。在结构体中#xff0c;默认的访问权限是公有的#xff08;public#xff09;#x…八股文
1.类和结构体的区别 在 C 中类class和结构体struct在语法上几乎是相同的唯一的区别是默认的访问权限。在结构体中默认的访问权限是公有的public而在类中默认的访问权限是私有的private。默认继承方式不同。 尽管结构体在设计上更偏向于数据的聚合而类更倾向于实现封装、继承和多态等面向对象编程的特性但实际上结构体也可以具有封装、继承和多态的特性。 2. 字符串长度 如果使用字符数组来存储字符串通常需要考虑字符串的结尾标志符 \0空字符ASCII码为0。在C中字符串是以空字符 \0 结尾的。 因此字符串的实际长度是字符数组长度减去1。这是因为最后一个字符用于存储空字符 \0用来表示字符串的结束。这个空字符告诉程序字符串的实际长度。 例如 #include iostream
#include cstringint main() {char str[10]; // 字符数组长度为10// 将字符串 Hello 复制到字符数组中strcpy(str, Hello);// 计算字符串长度int length strlen(str);// 输出字符串和长度std::cout String: str std::endl;std::cout Length: length std::endl;return 0;
}在这个例子中字符数组 str 的长度是10但实际存储的字符串是 “Hello”字符串长度是5。因为字符数组的最后一个位置被空字符 \0 占用。如果字符串的长度达到数组的最大长度需要确保数组足够大以容纳字符串和结尾的空字符。 3.宏定义
#define MAX_PRO1(A, B) ({ \int a A; \int b B; \a b ? a : b; \
})#define MAX_PRO3(A, B) ({ \typeof(A) a A; \typeof(B) b B; \(void) (a b); \a b ? a : b; \
})
4.动态扩容数组
#include iostreamtemplate typename T
class DynamicArray {
private:T* data; // 指向动态分配数组的指针size_t size; // 数组的当前元素个数size_t capacity; // 数组的当前容量public:// 构造函数DynamicArray() : data(nullptr), size(0), capacity(0) {}// 析构函数~DynamicArray() {delete[] data;}// 获取数组大小size_t getSize() const {return size;}// 添加元素到数组末尾void pushBack(const T value) {if (size capacity) {// 如果数组满了进行扩容reserve(capacity 0 ? 1 : 2 * capacity);}data[size] value;}// 获取数组元素T operator[](size_t index) {if (index size) {return data[index];} else {throw std::out_of_range(Index out of range);}}private:// 扩容数组void reserve(size_t newCapacity) {T* newData new T[newCapacity];for (size_t i 0; i size; i) {newData[i] data[i];}delete[] data;data newData;capacity newCapacity;}
};int main() {DynamicArrayint dynamicArray;for (int i 0; i 10; i) {dynamicArray.pushBack(i);}for (size_t i 0; i dynamicArray.getSize(); i) {std::cout dynamicArray[i] ;}return 0;
}
5.C函数多个返回值 以及默认值的情况。
默认值 #include iostream// 函数声明时为参数提供默认值
void greet(std::string name Guest, int age 0) {std::cout Hello, name ! Age: age std::endl;
}int main() {// 调用函数时不传递参数使用默认值greet(); // 输出: Hello, Guest! Age: 0// 调用函数时传递部分参数其他参数使用默认值greet(Alice); // 输出: Hello, Alice! Age: 0// 调用函数时传递所有参数不使用默认值greet(Bob, 25); // 输出: Hello, Bob! Age: 25return 0;
} 多个返回值 在C中一个函数一般只能有一个返回值。这是因为C语法规定函数的返回类型通过函数声明或定义中的返回类型指定只能是一个类型而函数执行完毕后只能返回一个值。 然而有一些方法可以实现类似于返回多个值的效果 使用结构体或类 可以定义一个结构体或类将多个值打包在一个结构体或类的对象中然后将这个对象作为函数的返回值。这样实际上就可以返回多个值了。 struct MultipleValues {int value1;double value2;
};MultipleValues myFunction() {MultipleValues result;result.value1 42;result.value2 3.14;return result;
}使用引用参数或指针参数 函数可以通过引用参数或指针参数来修改传递给它的变量的值从而实现对多个值的修改。 void myFunction(int value1, double value2) {value1 42;value2 3.14;
}int main() {int intValue;double doubleValue;myFunction(intValue, doubleValue);// 现在 intValue 和 doubleValue 包含了 myFunction 中设定的值return 0;
}这两种方法都能够在一定程度上模拟函数返回多个值的效果。选择哪种方法取决于实际的需求和代码结构。 6.机器字长 存储字长 32位机通常指的是机器字长Machine Word Length为32位。机器字长是指在一台计算机上CPU一次能够处理的二进制位数也就是它的寄存器宽度。这影响了CPU能够处理的数据的大小和寻址范围。 存储字长Storage Word Length是指计算机在内存中一次读取或写入的二进制位数。虽然这两个概念有相似之处但也存在区别 机器字长Machine Word Length 影响CPU的寄存器宽度决定CPU能够处理的单个数据块的大小。直接关系到寄存器的位数以及处理器能够执行的整数运算的范围。32位机器指的是机器字长为32位处理器一次能够处理32位的数据。 存储字长Storage Word Length 影响内存的读写宽度即一次从内存读取或写入的数据块的大小。决定了计算机在内存中的数据表示方式以及数据的传输速率。存储字长可以与机器字长相同但也可以不同。例如某些系统可能在内存中一次读取或写入64位的数据块而其机器字长仍然是32位。 总的来说机器字长主要涉及到处理器的寄存器宽度而存储字长则涉及到内存中的数据单元大小。在32位机器上这两者通常是相同的但在其他架构中可能存在不同的存储和机器字长。 不同位数的计算机体系结构可能会有不同的数据类型位数。计算机中的数据类型通常与机器字长相关而机器字长是指计算机处理器一次能够处理的二进制位数即寄存器宽度。
下面是一些常见的计算机体系结构和它们对应的典型的数据类型位数 32位计算机 通常使用32位的数据类型如int为32位。可能也支持其他位数的数据类型例如short为16位long为32位。 64位计算机 通常使用64位的数据类型如int为64位。同样可能支持其他位数的数据类型例如short为16位long为64位。 16位计算机 使用16位的数据类型如int为16位。较小的机器字长通常意味着更有限的数据表示范围。
具体的数据类型位数取决于编译器和计算机架构。C标准规定了最小的数据类型位数但允许编译器选择更大的位数。在不同的平台上可以通过查看编译器的文档或使用sizeof运算符来确定各种数据类型的确切位数。例如
#include iostreamint main() {std::cout Size of int: sizeof(int) * 8 bits std::endl;std::cout Size of long: sizeof(long) * 8 bits std::endl;// 可以输出其他数据类型的位数return 0;
}此程序将显示int和long数据类型在当前平台上的位数。
m位的机器的指针所占空间为m位 #####7. 常量指针与指针常量 哪个在前 哪个就不可变
常量指针定义又叫常指针常量的指针即这是个指向常量的指针这个常量是指针的值地址而不是地址指向的值。
关键点
常量指针指向的对象不能通过这个指针来修改可是仍然可以通过原来的声明修改常量指针可以被赋值为变量的地址之所以叫常量指针是限制了通过这个指针修改变量的值指针还可以指向别处因为指针本身只是个变量可以指向任意地址。
指针常量定义本质是一个常量而用指针修饰它。指针常量的值是指针这个值因为是常量所以不能被赋值。
关键点 它是个常量 指针所保存的地址可以改变然而指针所指向的值却不可以改变 指针本身是常量指向的地址不可以变化但是指向的地址所对应的内容可以变化。 使用前要初始化
int main() {
int a 10;
int b 10;//const修饰的是指针指针指向可以改指针指向的值不可以更改 常量指针 指向常量的指针指向的地址内容不可更改
const int * p1 a;
p1 b; //正确
//*p1 100; 报错//const修饰的是常量指针指向不可以改指针指向的值可以更改 指针常量
int * const p2 a;
//p2 b; //错误
*p2 100; //正确//const既修饰指针又修饰常量
const int * const p3 a;
//p3 b; //错误
//*p3 100; //错误system(pause);return 0;}
#####8. malloc free 以及 new delete的区别
#####malloc和free是C语言中用于内存分配和释放的函数而new和delete是C中对应的运算符。以下是它们之间的主要区别
malloc 和 freeC语言 语法 malloc: void* malloc(size_t size);free: void free(void* ptr); 类型安全 malloc 返回void*指针需要显式转换为实际的数据类型。不会调用构造函数和析构函数因此不适用于复杂对象。 初始化 malloc 分配的内存块中的内容不进行初始化可能包含随机值。 分配大小 需要指定要分配的内存大小。
new 和 deleteC语言 语法 new: Type* pointer new Type; 或 Type* pointer new Type[size];delete: delete pointer; 或 delete[] pointer; 类型安全 new 返回正确类型的指针不需要显式转换。能够调用对象的构造函数和析构函数。 初始化 new 分配的内存块中的内容会调用对象的构造函数保证对象处于有效状态。 分配大小 对于单个对象new 会自动计算大小。new 可以用于分配数组delete[] 用于释放整个数组。 异常处理 new 在分配失败时抛出 std::bad_alloc 异常需要使用 try-catch 进行处理。malloc 在分配失败时返回 NULL需要手动检查。
总体而言new 和 delete 提供了更多的功能和类型安全性适用于C中的对象。而 malloc 和 free 是C语言中的函数更低级用于简单的内存分配和释放。在C中推荐使用 new 和 delete 来管理动态内存。
先简述共同点再从类型安全初始化分配大小以及对象的创建和销毁的角度说明
9.左值和右值
左值Lvalue和右值Rvalue是C中的基本概念它们与表达式和对象的值的生命周期相关。以下是一个简洁而全面的回答
左值Lvalue
标识符 左值是一个具有标识符的表达式可以出现在赋值语句的左侧。持久性 具有持久性的对象其生命周期可以长时间存在。可寻址 可以取地址的表达式即可以使用取地址运算符 获取其地址。
右值Rvalue
临时性 右值是临时性的通常是在表达式求值后立即被销毁的临时对象。不能寻址 通常不能取地址的表达式尝试使用 取地址会导致编译错误。常常出现在赋值语句的右侧 右值通常出现在赋值语句的右侧作为赋值的源。
回答示例 左值和右值是C中用于描述表达式的术语。左值是具有标识符的表达式具有持久性可以取地址。典型的例子包括变量和通过引用访问的对象。右值是临时性的通常在表达式求值后立即被销毁。它通常出现在赋值语句的右侧是表达式的计算结果。***左值和右值的区别在于它们的生命周期和是否可寻址。***在C11及更高版本中右值引用Rvalue Reference的引入进一步强调了右值的重要性例如移动语义的实现。
关于右值引用可以补充说明在C11之后引入了右值引用这个新的类型可以通过 定义。右值引用允许我们更有效地处理右值如实现移动语义提高性能。
#####10. 四大类型转换
在C中有四种基本的类型转换通常被称为 “四大转换”它们分别是静态转换static_cast、动态转换dynamic_cast、常量转换const_cast、重新解释转换reinterpret_cast。这些转换提供了不同的功能涉及到不同的情景和要求。以下是一个简洁的回答
静态转换 (static_cast) 用于基本类型之间的转换如数值类型之间的转换。用于类层次结构中的向上转换派生类指针或引用转换为基类指针或引用但不进行运行时类型检查。用于明确类型转换例如在较为明确的场景下进行类型转换。
int num static_castint(3.14);
Base* basePtr static_castBase*(derivedPtr);动态转换 (dynamic_cast) 用于在类层次结构中进行运行时类型检查的向上和向下转换。仅用于具有虚函数的类通过运行时类型信息RTTI来判断类型。如果转换不安全返回指针或引用将为 nullptr 或引发 std::bad_cast 异常。
Derived* derivedPtr dynamic_castDerived*(basePtr);
if (derivedPtr ! nullptr) {// 安全地使用 derivedPtr
}常量转换 (const_cast) 用于添加或移除对象的 const 属性或 volatile 属性。不应该用于去除对象的真正的 const 属性这样的行为是未定义的。
const int value 42;
int* mutableValue const_castint*(value);重新解释转换 (reinterpret_cast) 用于将指针或引用从一种类型转换为另一种类型通常是不同类型之间的转换。这种转换非常底层潜在风险和不安全一般用于系统级编程和特定硬件操作。
int intValue 10;
double* doublePtr reinterpret_castdouble*(intValue);在回答时可以简要介绍每一种转换的主要用途和注意事项。强调在使用这些转换时需要慎重最好在确保安全性和可维护性的前提下使用。
11.函数重载
作用**函数名可以相同提高复用性
函数重载满足条件
同一个作用域下函数名称相同函数参数类型不同 或者 个数不同 或者 顺序不同
注意: 函数的返回值不可以作为函数重载的条件
12.面向对象的三大特性以及好处
在C中面向对象编程OOP的三大特性分别是封装Encapsulation、继承Inheritance、和多态Polymorphism。 封装Encapsulation 好处 将数据和操作封装在类中隐藏了内部实现的细节提高了代码的可维护性和可读性。通过访问修饰符如public、private、protected控制对类的成员的访问权限提高了安全性。可以实现信息隐藏使得对象的具体实现对外部是透明的用户只需要关心对象的接口和行为。 继承Inheritance 好处 提高了代码的重用性通过创建新类并继承现有类的属性和方法可以避免重复编写相似的代码。支持多层次的类关系提高了代码的层次结构和组织性。提供了多态的基础使得可以用父类的指针或引用来操作子类的对象。 多态Polymorphism 好处 提高了代码的灵活性同一操作可以用于不同的对象而不需要知道对象的具体类型。可以通过继承和虚函数实现运行时多态允许子类重写父类的方法实现特定的行为。支持函数重载和运算符重载提高了代码的可读性和一致性。
总体而言面向对象编程的这三大特性有助于提高代码的可维护性、可读性、重用性和灵活性。它们提供了一种结构化的方法使得程序员能够更好地组织和设计代码同时通过封装、继承和多态实现抽象、简化和模块化。
13.构造函数与析构函数 构造函数主要作用在于创建对象时为对象的成员属性赋值构造函数由编译器自动调用无须手动调用。 析构函数主要作用在于对象销毁前系统自动调用执行一些清理工作。 构造函数语法**类名(){} 构造函数没有返回值也不写void 函数名称与类名相同 构造函数可以有参数因此可以发生重载 程序在调用对象时候会自动调用构造无须手动调用,而且只会调用一次 析构函数 调用时机 析构函数会在对象生命周期结束时被调用这可能是在对象离开其作用域时或者通过 delete 运算符显式删除动态分配的对象时。 只调用一次 每个对象的析构函数只会被调用一次。当对象的生命周期结束时析构函数会被自动调用清理资源并执行必要的操作。如果对象是动态分配的使用 new 创建则在调用 delete 时会触发析构函数的调用。 析构函数语法**~类名(){} 构造函数没有返回值也不写void 函数名称在类名加~ 构造函数不可以有参数因此不可以发生重载 在对象生命周期结束时会自动调用析构函数。
14.构造函数的分类以及深拷贝与浅拷贝
两种分类方式
按参数分为 有参构造和无参构造
按类型分为 普通构造和拷贝构造
三种调用方式
括号法
显示法
隐式转换法
深浅拷贝
浅拷贝简单的赋值拷贝操作
***深拷贝在堆区重新申请空间进行拷贝操作 ***
为了避免浅拷贝时堆内存数据重复释放
深拷贝Deep Copy和浅拷贝Shallow Copy是涉及到对象拷贝的两个概念主要用于描述在复制对象时如何处理对象内部的数据。以下是它们的区别
浅拷贝Shallow Copy 浅拷贝只复制对象的值而不复制对象内部的动态分配的资源。对象的成员变量如果是指针类型浅拷贝仅复制指针的值而不复制指针所指向的内存。这意味着两个对象将共享同一块内存。如果其中一个对象修改了共享内存中的数据另一个对象也会受到影响。
class ShallowCopyExample {
public:int* data;// 构造函数ShallowCopyExample(int value) {data new int(value);}// 拷贝构造函数浅拷贝ShallowCopyExample(const ShallowCopyExample other) {data other.data; // 浅拷贝共享同一块内存}// 析构函数~ShallowCopyExample() {delete data;}
};深拷贝Deep Copy 深拷贝会复制对象的值并且复制对象内部的动态分配的资源而不是共享同一块内存。对象的成员变量如果是指针类型深拷贝会为指针指向的内存分配新的空间并将原始数据复制到新分配的内存中。这样两个对象拥有各自独立的内存修改其中一个对象不会影响另一个对象。
class DeepCopyExample {
public:int* data;// 构造函数DeepCopyExample(int value) {data new int(value);}// 拷贝构造函数深拷贝DeepCopyExample(const DeepCopyExample other) {data new int(*(other.data)); // 深拷贝分配新的内存}// 析构函数~DeepCopyExample() {delete data;}
};在实际编程中当类包含动态分配的资源时经常需要谨慎考虑深拷贝和浅拷贝的问题以确保对象之间的独立性。
深拷贝和浅拷贝都是初始化对象属性的方式。浅拷贝仅复制对象的值而不复制对象动态分配的资源。深拷贝既会拷贝对象的值也会拷贝对象动态分配的资源确保新对象是原对象的独立副本。如果一个类在堆区有成员变量使用深拷贝是必要的因为浅拷贝会导致多个对象共享同一块内存可能引发潜在的问题。深拷贝的原理是重新在堆区开辟一个空间并将原对象的值和动态分配的资源复制到新的空间中确保对象的独立性和完整性。
#####15.静态成员
静态成员就是在成员变量和成员函数前加上关键字static称为静态成员
静态成员分为
静态成员变量 所有对象共享同一份数据在编译阶段分配内存类内声明类外初始化 静态成员函数 所有对象共享同一个函数静态成员函数只能访问静态成员变量
16.c的内存模型
成员变量与成员方法分开存储
好处
1.属性与行为相分离:提高可维护性 增强可读性
2.节省内存属性表明对象的状态而成员函数是所有对象共享的。
3.生命周期不同
4.权限访问控制。
17.友元
友元是一种在C中的特殊机制它允许一个函数或者类访问另一个类的私有成员。友元可以提供对类的某些成员的访问权限打破了类的封装性。友元可以是一个函数一个类或者一个类中的成员函数。然而要谨慎使用友元因为过度使用可能导致代码的复杂性增加破坏了封装性使代码变得难以理解和维护。因此在设计中应该限制友元的使用确保它只在必要的情况下被使用。
#####18.运算符重载
运算符重载是C中的一项特性它允许程序员重新定义或者扩展基本的运算符使其能够用于自定义数据类型。基本的运算符通常仅对内置数据类型进行操作但运算符重载使得我们可以定义类的成员函数或全局函数以实现对自定义数据类型的运算。通过运算符重载我们能够提高代码的可读性和表达性使得自定义类型的对象可以像内置类型一样进行直观的运算操作。这种能力允许我们在自定义类型上使用类似于 、-、* 等运算符从而使代码更加简洁、优雅并且更符合直觉。
#####19.子类继承父类的方式
子类继承父类有三种方式公有继承受保护的继承私有继承。无论哪种继承方式子类均可以访问到父类的非私有成员。但子类的继承的成员的可见性会发生变化。如果是共有继承那么可见性与父类一致如果是受保护的继承则除了父类的私有成员其他的可见性都变成了受保护如果是私有继承那么可见性均变为私有。
1.受保护权限和所有权限的区别主要在于派生类继承对父类成员的可见性的区别而类外的可见性没有区别
2.继承中 先调用父类构造函数再调用子类构造函数析构顺序与构造相反 访问子类同名成员 直接访问即可访问父类同名成员 需要加作用域c支持多继承菱形继承菱形继承带来的主要问题是子类继承两份相同的数据导致资源浪费以及毫无意义利用虚继承可以解决菱形继承问题
#####20.虚继承
虚继承是C中用于解决菱形继承问题Diamond Problem的一种技术。当一个类被虚继承时它的派生类只会继承一份基类的实例而不是多份。这样可以防止由于多次继承同一基类而导致的二义性和资源浪费。
#####21.多态的概念
多态是面向对象编程的一项重要特性它允许通过父类指针或引用来访问派生类对象通过同一个父类接口可以调用不同子类对象的属性或方法。多态的实现条件主要有三个首先父类必须有虚函数其次子类必须重写这个虚函数最后我们通过父类指针或引用指向子类对象。
多态的核心在于动态绑定这意味着在运行时才确定调用哪个函数。当我们通过父类指针调用虚函数时程序会在运行时根据指针所指的具体对象类型来动态地确定调用的是哪个函数。这样同一份代码可以处理不同类型的对象从而提高了代码的灵活性和可扩展性
22.虚函数指针 虚函数表
在包含虚函数的类中编译器在对象的内存布局中插入了一个称为虚函数指针vptr的指针。这个指针指向该类的虚函数表虚函数表是一个数组包含了该类及其基类的虚函数地址。对于每一个具体的对象虚函数指针都会被初始化为指向其所属类的虚函数表。
在运行时当我们通过基类指针或引用调用虚函数时程序会使用虚函数指针来访问虚函数表。动态绑定的过程发生在运行时通过查询虚函数表确定到底调用哪个函数。这意味着即使是通过基类指针或引用调用虚函数实际调用的是与对象的实际类型相对应的函数而不是基类中的函数。
***总的来说虚函数指针和虚函数表提供了一种在运行时动态地确定调用哪个虚函数的机制实现了多态性 ***
23.模板的概念
模板包括函数模板与类模板 可以将类型参数化这允许我们编写出与数据类型无关的代码增强了代码复用与开发效率 同时模板也是类型安全的
#####24. 友元
友元作为类 注意要引用一个类那个对象要先声明
#include iostream
using namespace std;/*
定义一个类B,然后B想要访问A里面的私有成员
那么
1.应该先声明B,因为B要在A中声明友元
2.申明并定义A
3.定义B
*/
class B;
class A {
private:int a;
public:A(int a) {this-a a;}friend class B;};class B {
private:int b;
public:B(int b) {this-b b;}void print_A(const A a) {cout a.a;}};
int main() {A a(50);B b(20);b.print_A(a);return 0;
}函数作为友元
*** 全局函数作为友元时友元声明必须在类的内部进行***
#include iostream
using namespace std;/*
函数作为友元
与类作为友元的区别是
函数友元的声明在类内部 类作为友元可以在外部或者内部。
*/class A {
private:int a;
public:A(int a) {this-a a;}friend void print_A(const A a);};void print_A(const A a) {cout a.a endl;}int main() {A a(20);print_A(a);return 0;
}
成员函数作为友元: 类的成员函数在另一个类中声明