sizeof运算符和strlen函数的区别是什么
- 使用时机:
sizeof在编译时确定大小,而strlen在运行时计算字符串长度。 - 用途:
sizeof用于获取内存占用大小,strlen用于获取字符串长度。 - 对象:
sizeof可以用于任何数据类型,而strlen只能用于以 null 结尾的字符串。 - 返回值:
sizeof返回的是字节数,而strlen返回的是字符数。 - 性能:
sizeof没有运行时开销,因为它在编译时就已经确定;strlen需要遍历整个字符串直到找到 null 字符,因此有运行时开销。
strcpy,memcpy
strcpy函数将源字符串(包括结尾的 null 字符 \0)复制到目标字符串中。
- 重叠字符串:如果源字符串和目标字符串有重叠,
strcpy可能会导致未定义的行为。在这种情况下,应使用strncpy函数。 - 安全替代:为了更安全地复制字符串,可以使用
strncpy函数,它允许指定最大复制的字符数。但是,即使使用strncpy,也需要确保正确处理字符串的结尾。
memcpy是一个内存复制函数,它将指定数量的字节从源内存区域复制到目标内存区域。
memcpy 不会停止复制操作来检查源或目标内存区域中的 null 字符。 它可以用于复制任意类型的数据,包括结构体和数组。
malloc和new
前者只分配内存,不会调用任何构造函数。- 返回 void* 类型的指针,需要显式地转换为需要的指针类型。
- 需要手动调用
free来释放内存。
后者分配内存,并调用对象的构造函数。- 返回正确类型的指针,无需显式类型转换。
- 需要使用
delete来释放单个对象的内存,使用delete[]来释放数组的内存。
虚函数与纯虚函数
-
虚函数(Virtual Function) :
- 虚函数是一个在基类中声明为
virtual的函数,可以在派生类中被重写。 - 虚函数可以有实现(即函数体),也可以没有实现(即只有声明)。
- 一个类如果包含至少一个虚函数,它就被称为多态类(polymorphic class)。
- 虚函数的调用在运行时动态绑定,即根据对象的实际类型来调用相应的函数。
- 虚函数是一个在基类中声明为
-
纯虚函数(Pure Virtual Function) :
- 纯虚函数是一个在基类中声明为
virtual并且没有实现的函数,其声明的末尾有两个关键字= 0。 - 包含至少一个纯虚函数的类被称为抽象类(abstract class),抽象类不能被实例化。
- 纯虚函数的主要目的是作为接口,强制派生类提供特定的实现。
- 派生类必须重写所有纯虚函数,除非派生类也是抽象类。
- 纯虚函数是一个在基类中声明为
示例:
cpp
class Base {
public:
virtual void func1() { /* 实现 */ } // 虚函数
virtual void func2() = 0; // 纯虚函数
};
class Derived : public Base {
public:
void func1() override { /* 重写实现 */ }
void func2() override { /* 实现 */ } // 必须提供实现
};
区别:
- 实现:虚函数可以有实现,而纯虚函数没有实现。
- 实例化:包含虚函数的类可以被实例化,而包含纯虚函数的类不能被实例化,它们是抽象类。
- 目的:虚函数用于实现多态性,允许在基类指针或引用上调用函数时动态绑定到派生类的实现。纯虚函数用于定义接口,确保派生类实现某些函数。
- 重写:派生类可以不重写虚函数(除非需要改变行为),但必须重写纯虚函数(除非派生类也是抽象类)。
goto语句作用
在 C++ 中,goto 语句是一种无条件的跳转语句,它允许程序的控制流跳转到同一作用域内的某个标签处。goto 通常用于跳出深层嵌套的循环或跳出复杂的代码块。
语法:
goto 标签名;
示例:
// 外层循环
for (i = 0; i < 5; ++i) {
// 中层循环
for (j = 0; j < 5; ++j) {
// 内层循环
for (k = 0; k < 5; ++k) {
if (i * j * k > 10) {
std::cout << "Jumping out of loops!" << std::endl;
goto end; // 跳转到 end 标签
}
}
}
}
// 如果没有跳转,执行到这里
std::cout << "This will not be printed." << std::endl;
end:
std::cout << "This will be printed after the goto statement." << std::endl;
return 0;
}
注意事项:
-
标签定义:标签是由标识符和冒号组成的,它必须位于
goto语句之前,且在同一个作用域内。 -
跨函数跳转:
goto不能跳转到另一个函数中的标签。 -
跨作用域跳转:
goto不能跳转到不同作用域的标签,例如不能从函数内部跳转到函数外部。 -
跳过初始化:使用
goto可能会跳过变量的初始化,这可能导致未定义的行为。
goto 在某些情况下可能有用,但一般建议尽量避免使用它,除非没有更好的替代方案
nullptr
用于声明一个指向特定类型的空指针
可以被隐式转换为任何指针类型,但不会转换为整数类型;
nullptr 可以用作任何指针类型的初始值,但它必须被转换为相应的指针类型。
STRUCT和CLASS的区别
-
默认访问权限:
struct的成员默认是public,这意味着在没有指定访问修饰符的情况下,struct的成员可以被任何访问该struct的代码访问。class的成员默认是private,这意味着在没有指定访问修饰符的情况下,class的成员只能被类的内部以及友元函数访问。
-
继承访问权限:
- 当一个
struct继承自另一个struct或class时,基类的private成员在派生struct中仍然是private,基类的protected成员在派生struct中变为public。 - 当一个
class继承自另一个class或struct时,基类的private成员在派生class中仍然是private,基类的protected成员在派生class中仍然是protected。
- 当一个
-
设计意图:
struct通常用于表示简单的数据集合,它强调数据的聚合,而不是数据的封装和抽象。class通常用于表示复杂的数据类型和对象的封装,它强调数据的隐藏和操作数据的函数的封装。
-
布局:
- 在某些编译器实现中,
struct和class在内存布局上可能有所不同,尽管这并不是标准规定的行为。
- 在某些编译器实现中,
-
历史和习惯用法:
- 在 C++ 的早期版本中,
struct没有方法和继承,只能用来定义纯数据结构。随着 C++ 的发展,struct获得了与class相同的功能。 - 在 C 语言中,
struct只能包含数据,不能包含函数。在 C++ 中,struct和class都可以包含数据和函数。
- 在 C++ 的早期版本中,
智能指针和指针的区别
-
内存管理:
- 智能指针:自动管理内存,当智能指针对象的生命周期结束时,会自动释放它所指向的内存。
- 普通指针:需要程序员手动使用
new和delete操作符来分配和释放内存。
-
类型:
- 智能指针:实际上是一个模板类的对象,例如
std::unique_ptr、std::shared_ptr和std::weak_ptr。 - 普通指针:是直接指向某个数据类型的内存地址的变量。
- 智能指针:实际上是一个模板类的对象,例如
-
所有权:
-
智能指针:
std::unique_ptr表示独占所有权,不允许复制,只能移动。std::shared_ptr使用引用计数机制,表示多个指针可以共享同一个对象。
-
普通指针:没有所有权的概念,需要程序员自己确保资源的正确释放。
-
-
异常安全:
- 智能指针:通常提供异常安全保证,即使在抛出异常时也能确保内存被释放。
- 普通指针:如果异常发生时没有正确释放内存,可能会导致内存泄漏。
-
使用场景:
- 智能指针:用于管理动态分配的内存,避免手动管理内存的错误。
- 普通指针:用于各种场景,包括但不限于指向静态分配、栈分配或动态分配的内存。
-
性能开销:
- 智能指针:有一定的性能开销,特别是在
std::shared_ptr中,因为需要维护引用计数。 - 普通指针:没有额外的性能开销。
- 智能指针:有一定的性能开销,特别是在
-
兼容性:
- 智能指针:不能直接与 C 语言 API 一起使用,因为 C++ 的智能指针对于 C 语言来说是不透明的。
- 普通指针:可以与 C 语言 API 无缝交互。
-
功能:
- 智能指针:除了基本的指针功能外,还提供了额外的功能,如自动内存管理、引用计数、自定义删除器等。
- 普通指针:只有基本的指针功能。
线程之间的通信方式
-
互斥量(Mutex) :
- 用于保护共享数据,确保同一时间只有一个线程可以访问临界区。
-
锁(Locks) :
- 例如
std::lock_guard、std::unique_lock,它们使用 Mutex 来管理对共享资源的访问。
- 例如
-
条件变量(Condition Variables) :
- 允许线程等待某些条件成立,当其他线程更改了这些条件时,条件变量可以被用来唤醒等待的线程。
-
线程安全队列:
- 一种线程安全的队列实现,可以在生产者-消费者场景中使用。
-
原子操作(Atomic Operations) :
std::atomic提供了一种机制,使得单个变量的操作是原子的,即不可中断的。
-
信号量(Semaphores) :
- 用于控制对共享资源的访问数量。
-
屏障(Barriers) :
- 确保所有线程都到达某个点后再继续执行。
-
Future 和 Promise:
std::future和std::promise允许线程之间传递值或异常。
-
线程局部存储(Thread-Local Storage, TLS) :
- 使用
thread_local关键字声明的变量,每个线程都有其独立的实例。
- 使用
-
共享内存:
- 直接通过共享内存地址来通信,需要配合同步机制使用。
-
消息传递接口:
- 如 POSIX 消息队列,可以用来在不同线程或进程之间传递消息。
-
管道(Pipes) :
- 可以用于线程之间的简单通信。
-
套接字(Sockets) :
- 在分布式系统中,线程可以通过网络套接字进行通信。
-
文件映射(Memory-Mapped Files) :
- 允许多个线程通过文件系统访问同一块内存区域。