这个是面向面试的,更详细的可以去看我总结的另一篇:C++虚函数详解
C++ 中虚函数的实现涉及到虚函数表(vtable)和虚函数指针(vptr),这是实现多态性的关键。虚函数表和虚函数指针允许程序在运行时确定要调用的函数,而不是在编译时确定。
vptr
),该指针指向对象所属类的虚函数表。这个vptr
通常位于对象的内存布局的开头。vptr
找到正确的虚函数表,然后在虚函数表中查找要调用的函数的地址,最后执行该函数。vptr
,然后从虚函数表中找到要调用的函数。在 C++ 中,std::vector
是一个动态数组,它可以自动扩容以容纳更多元素。其动态扩容的底层机制涉及到内存分配和复制元素。详细步骤:
当创建一个空的 std::vector
或者向一个已有的 std::vector
添加元素时,std::vector
会首先分配一块初始的内存空间。这个内存块的大小通常是小于或等于系统的内存页大小。这是一个较小的固定大小的内存块,称为容量(capacity),通常远远大于 std::vector
中当前元素的数量。它能容纳更多元素,以减少频繁的内存分配操作。2. 元素添加:
当你向 std::vector
添加元素时,如果元素数量(大小,size)等于容量(capacity),则需要触发扩容操作。3. 扩容操作:
std::vector
的扩容操作会分配一块新的内存区域,通常是当前容量的两倍。这个过程确保了容器在元素添加时具有线性复杂度,即 O(1) 的均摊时间复杂度,因为扩容操作不会频繁发生,而且每次的扩容是成倍增加的。4. 重新分配内存:
需要注意的是,每次扩容后,std::vector
的容量会超过实际元素的数量。这种重新分配内存和复制数据的操作可能导致性能开销。因此,在对 std::vector
进行大量元素添加操作时,可以通过 reserve()
函数来预先分配足够的内存,以避免不必要的扩容和复制操作。
std::vector
:std::vector
存储普通数据类型(如整数、浮点数、自定义结构体等)时,容器会在扩容时分配新的内存,并将现有数据复制到新内存中。std::vector
:std::vector
存储指针时,容器会在扩容时仅复制指针,而不会复制指针所指向的对象。这是因为指针本身的大小是固定的,通常为 4 字节或 8 字节(根据系统架构),因此拷贝指针的开销很小。std::vector
中的指针指向的对象时,另一个 std::vector
中的相应指针也会反映这些修改。<span style="letter-spacing: 0.578px;text-decoration: none;font-size: 16px;">std::atomic
类型可以用于实现原子操作,确保多个进程在更新共享内存时不会互相干扰。它们并不存储关于分配内存块大小的信息。这是因为这两个函数设计时的出发点是提供一个轻量级的、固定的内存管理机制,以降低运行时的开销。这也就意味着,一旦你使用 malloc
分配了内存,你需要自行追踪该内存块的大小。有几种常见的方法来做到这一点:
size_t block_size = 100; // 假设内存块大小是100字节
void* ptr = malloc(block_size);
// ...
free(ptr);
size_t block_size = 100;
void* ptr = malloc(block_size + sizeof(size_t)); // 额外存储大小信息
*((size_t*)ptr) = block_size; // 存储大小信息
// ...
size_t size = *((size_t*)ptr); // 从头信息中获取大小
free(ptr);
需要注意的是,使用malloc
和free
时,确保精确地跟踪内存块的大小至关重要,以避免内存泄漏和未定义的行为。
线程池主要用于管理和重用线程以执行多个任务,从而提高多线程应用程序的效率和性能。线程池通常由线程队列和一组工作线程组成。
它的设计和使用的好处?
线程池通常应用于需要执行大量独立任务的场景,如服务器应用、网络通信、图像处理等。使用线程池可以提高程序的并发性、性能和可维护性。不过,线程池的设计和配置需要根据具体应用的需求和系统特性来调整,包括线程数量、任务队列的大小、任务优先级等。
在C++中,基类(父类)的析构函数通常被声明为虚函数,这是为了实现多态和正确的资源释放。
Base* basePtr = new Derived;
delete basePtr; // 如果析构函数不是虚函数,只会调用Base的析构函数
如果Base
类的析构函数不是虚函数,那么上述代码将调用Base
类的析构函数,而Derived
类中的资源可能不会被正确释放,导致资源泄漏。
.h
或 .hpp
为扩展名。示例:
// mymacros.h
#ifndef MYMACROS_H
#define MYMACROS_H
#define MAX(a, b) ((a) > (b) ? (a) : (b)
#endif
在源文件中包含头文件:
// main.cpp
#include "mymacros.h"
#include <iostream>
int main() {
int x = 5, y = 7;
int result = MAX(x, y);
std::cout << "Max: " << result << std::endl;
return 0;
}
// main.cpp
#include <iostream>
#define PI 3.14159
int main() {
double radius = 5.0;
double area = PI * radius * radius;
std::cout << "Area: " << area << std::endl;
return 0;
}
<span style="letter-spacing: 0.578px;text-decoration: none;font-size: 16px;">-D
来定义宏。这种宏定义仅适用于特定的编译。g++ -DDEBUG=1 main.cpp -o myprogram
宏定义通常用于在源代码中创建简单的替代标识符,以便在编译时进行文本替换。然而,宏的滥用可能导致代码的可读性下降,因此需要慎重使用。
在Qt中,信号与槽是一种强大的事件处理机制,用于实现对象之间的通信。以下是Qt中信号与槽的连接方式的详细说明:1. 手动连接:这是最基本的连接方式,通常用于在运行时建立连接。你可以使用QObject
的connect()
方法手动连接信号和槽。这种方式需要使用SIGNAL()
和SLOT()
宏来指定信号和槽。例如:
QObject::connect(sender, SIGNAL(senderSignal()), receiver, SLOT(receiverSlot()));
SIGNAL()
和 SLOT()
宏,而是使用槽函数的指针。这种方式更加类型安全,编译器可以检测连接的有效性。示例:QObject::connect(sender, &SenderClass::senderSignal, receiver, &ReceiverClass::receiverSlot);
QObject::connect(sender, &SenderClass::senderSignal, [=]() {
// 处理信号触发的操作
});
用于接收和处理来自操作系统的消息,例如鼠标事件、键盘事件、窗口事件等。消息循环允许应用程序响应用户输入和操作系统的通知,以便进行适当的处理。详细解释:
智能指针是一种用于管理动态分配内存的智能工具,可以自动进行内存管理,避免了常见的内存泄漏问题。
std::shared_ptr
:std::shared_ptr
使用引用计数的技术来管理内存。多个shared_ptr
对象可以共享同一个动态分配的对象,它们都维护一个引用计数,当引用计数变为零时,对象的内存将被自动释放。shared_ptr
是一个合适的选择。std::unique_ptr
:std::unique_ptr
采用独占拥有权的模式,每个unique_ptr
对象独立拥有所管理的资源。当一个unique_ptr
对象离开作用域或被销毁时,它的资源会被释放。unique_ptr
是首选。std::weak_ptr
:std::weak_ptr
是为了解决std::shared_ptr
的循环引用问题而引入的。它允许访问由shared_ptr
管理的资源,但不会增加引用计数。如果所有的shared_ptr
都离开作用域,资源将被释放。weak_ptr
可以用于打破这种循环引用。应用场景:- 使用std::shared_ptr
来管理一个共享的配置对象,多个模块都需要访问该配置。
std::unique_ptr
来管理一个文件句柄,确保只有一个对象拥有文件句柄。std::weak_ptr
来处理图形对象之间的引用关系,防止循环引用导致的内存泄漏。std::shared_ptr
、std::unique_ptr
等)可以大大减少内存泄漏的可能性。这些指针会在对象不再需要时自动释放内存。new
后应使用delete
,使用new[]
后应使用delete[]
,并避免悬挂指针。<span style="letter-spacing: 0.578px;text-decoration: none;font-size: 16px;">new
和<span style="letter-spacing: 0.578px;text-decoration: none;font-size: 16px;">malloc
等分配内存的返回值是否为<span style="letter-spacing: 0.578px;text-decoration: none;font-size: 16px;">nullptr
,以处理分配失败的情况。前边的常用,可以再详细学一学,后边那几个不常用,作为了解。
它们都是多线程编程中用于同步和线程间通信的重要工具。它们通常一起使用以实现线程之间的协调。互斥锁(Mutex):
std::mutex
类来创建互斥锁。lock()
(获取锁)、unlock()
(释放锁)和try_lock()
(尝试获取锁,不会阻塞)。#include <iostream>
#include <mutex>
std::mutex mtx;
void someFunction() {
std::lock_guard<std::mutex> lock(mtx); // 获取锁
// 访问共享资源
// ...
} // lock析构时会释放锁
条件变量(Condition Variable):
std::condition_variable
类来创建条件变量。
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void waitForCondition() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [] { return ready; }); // 等待条件变为真
// 条件满足,执行后续操作
}
void notifyCondition() {
std::this_thread::sleep_for(std::chrono::seconds(2));
std::lock_guard<std::mutex> lock(mtx);
ready = true;
cv.notify_one(); // 通知等待的线程条件已满足
}
int main() {
std::thread t1(waitForCondition);
std::thread t2(notifyCondition);
t1.join();
t2.join();
return 0;
}
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8