当前位置:网站首页 > C++编程 > 正文

c++练手_c语言编程题库

前言

互联网技术面一般会涉及手撕算法,除了常见的力扣 Hot100、剑指 Offer 外,可能还会有一些常见的设计题,主要考察基本代码能力,本文整理了一些 C++ 语言常见的题目,大部分版本为简单的实现,面试的时候建议和面试官沟通确定是否需要进阶实现。个人手动整理,如有不足,欢迎批评指正。

单例模式

常见问题:

  • 绝对线程安全的单例(Meyers Singeleton)
  • 设计一个线程安全的单例模式
  • 懒汉单例模式的线程安全问题
  • 为什么要用锁加双重判断,只判断一次不可以吗?

什么是单例模式

在一个项目中,全局范围内,某个类的实例有且仅有一个,通过这个唯一实例向其他模块提供数据的全局访问,这种模式就叫单例模式。单例模式的典型应用就是任务队列。

如何实现一个单例模式

  • 使用单例模式,首先要保证这个类的实例有且仅有一个,这个时候构造函数都需要设置为私有或删除的,因为不能通过构造函数来创建新的对象。
  • 具体实现的时候,有两种实现方式:
    • 饿汉式:饿汉模式就是在类加载的时候立刻进行实例化,这样就得到了一个唯一的可用对象。
    • 懒汉式:是在类加载的时候不去创建这个唯一的实例,而是在需要使用的时候再进行实例化

饿汉式单例模式实现代码

  • 类的静态成员变量放在外面进行初始化,然后通过 getInstance() 函数返回实例
// 饿汉模式 class TaskQueue { public: // = delete 代表函数禁用, 也可以将其访问权限设置为私有 TaskQueue(const TaskQueue& obj) = delete; TaskQueue& operator=(const TaskQueue& obj) = delete; static TaskQueue* getInstance() { return m_taskQ; } private: TaskQueue() = default; static TaskQueue* m_taskQ; }; // 静态成员初始化放到类外部处理 TaskQueue* TaskQueue::m_taskQ = new TaskQueue; int main() { TaskQueue* obj = TaskQueue::getInstance(); } 

懒汉式单例模式实现代码(基础版)

  • 基本思路是在调用 getInstance() 函数的时候,判断类是否实例化了,没有的话,进行实例化;
  • 但是下面的代码会有线程安全问题:
// 懒汉模式 class TaskQueue { public: // = delete 代表函数禁用, 也可以将其访问权限设置为私有 TaskQueue(const TaskQueue& obj) = delete; TaskQueue& operator=(const TaskQueue& obj) = delete; static TaskQueue* getInstance() { if(m_taskQ == nullptr) { m_taskQ = new TaskQueue; } return m_taskQ; } private: TaskQueue() = default; static TaskQueue* m_taskQ; }; TaskQueue* TaskQueue::m_taskQ = nullptr; 

双重检查锁单例模式

为什么是双重检查?如果是只检查一次的话,先上锁然后在判断是否实例化,这样的话后续其他线程在调用 getInstance() 函数的时候,都会阻塞在这行,效率比较低下。

class TaskQueue { public: // = delete 代表函数禁用, 也可以将其访问权限设置为私有 TaskQueue(const TaskQueue& obj) = delete; TaskQueue& operator=(const TaskQueue& obj) = delete; static TaskQueue* getInstance() { if (m_taskQ == nullptr) { m_mutex.lock(); if (m_taskQ == nullptr) { m_taskQ = new TaskQueue; } m_mutex.unlock(); } return m_taskQ; } private: TaskQueue() = default; static TaskQueue* m_taskQ; static mutex m_mutex; }; TaskQueue* TaskQueue::m_taskQ = nullptr; mutex TaskQueue::m_mutex; 

C++ 双重检查锁单例模式一定是安全的吗?

  • 不是,因为指令可能会重排,假设第一步是分配 TaskQueue 内存,第二步是在内存中构造 TaskQueue 对象,第三步是 m_taskQ 指针指向分配的内存;
  • 重排后可能导致 m_taskQ 先指向分配的内存,之后在构造对象,而到了第二步,如果这个时候线程时间片耗尽,转而执行另外一个线程,另外一个线程在检查的时候,发现指针不为空,但是实际上这个时候对象还没初始化完成,这就出现问题了;
  • 解决方法:
    • 使用 call_onceonce_flag 来确保初始化只发生一次;
    • 另外一种是使用局部静态变量,因为 C++11 规定:如果指令逻辑进入一个未被初始化的声明变量,所有并发执行应当等待该变量完成初始化。所以局部静态变量也是线程安全的。
class Singleton { private: static Singleton instance; static std::once_flag onceFlag; explicit Singleton() { // 构造函数私有化,防止直接实例化 } public: static Singleton& getInstance() { std::call_once(onceFlag, []() { instance = Singleton(); }); return instance; } }; // 静态成员初始化 Singleton Singleton::instance; std::once_flag Singleton::onceFlag; // 示例使用 int main() { Singleton& singleton = Singleton::getInstance(); // 使用singleton... return 0; } 

以下是使用局部静态变量:

class TaskQueue { public: // = delete 代表函数禁用, 也可以将其访问权限设置为私有 TaskQueue(const TaskQueue& obj) = delete; TaskQueue& operator=(const TaskQueue& obj) = delete; static TaskQueue* getInstance() { static TaskQueue taskQ; return &taskQ; } void print() { cout << "hello, world!!!" << endl; } private: TaskQueue() = default; }; int main() { TaskQueue* queue = TaskQueue::getInstance(); queue->print(); return 0; } 

多线程交替打印 abc

问题:使用 3 个线程交替打印输出 abc 字符

主要考察多线程的使用,因为需要交替打印,这里需要对线程进行阻塞和等待,用一个全局的互斥锁,一个条件变量,以及一个变量 loop 来控制交替的轮次。代码如下:

// thread_abc.cpp #include <iostream> #include <thread> #include <mutex> #include <condition_variable> // 使用互斥锁确保线程安全 std::mutex mtx; std::condition_variable cv; int loop = 0; // 打印函数 void printChar(int curr, int count) { std::unique_lock<std::mutex> lock(mtx); int i; while (i < count) { // 不是当前线程执行的时候,该线程进行阻塞等待 while (loop != curr) { cv.wait(lock); } loop = (loop + 1) % 3; // 转移给下一个线程 char c = 'a' + curr; std::cout << c; ++i; cv.notify_all(); // 唤醒所有阻塞的线程 } } int main() { // 创建三个线程,交替打印 'a', 'b', 'c' 共10次 std::thread threadA(printChar, 0, 10); std::thread threadB(printChar, 1, 10); std::thread threadC(printChar, 2, 10); // 等待所有线程执行完毕 threadA.join(); threadB.join(); threadC.join(); std::cout << std::endl; // 输出换行以使结果更清晰 return 0; } 

生产者消费者模型

一个简单的生产者消费者模型,缓冲区大小为 10,生产者不断的往缓冲区中加入数据,直到容量满,消费者不断的从缓冲区中读取数据,一旦缓冲区为空就会阻塞。

#include <algorithm> #include <iostream> #include <thread> #include <condition_variable> #include <mutex> #include <queue> using namespace std; int N = 100; const int MAX_SIZE = 10; std::condition_variable cv; std::mutex mtx; std::queue<int> que; void producer() { for (int i = 0; i < N; ++i) { std::unique_lock<mutex> lk(mtx); cv.wait(lk, []() { return que.size() <= MAX_SIZE; }); std::cout << std::this_thread::get_id() << " 生成数据 " << i << endl; que.push(i); cv.notify_one(); } } void consumer() { for (int i = 0; i < N; ++i) { std::unique_lock<mutex> lk(mtx); cv.wait(lk, []() { return !que.empty(); }); std::cout << std::this_thread::get_id() << " 消耗数据 " << que.front() << endl; que.pop(); cv.notify_one(); } } int main() { std::thread t1(producer); std::thread t2(consumer); t1.join(); t2.join(); } 

线程池

基础版线程池

一个比较简单的线程池,提交的任务函数类型为 std::function<void()>,实现如下:

#include <iostream> #include <vector> #include <queue> #include <functional> #include <thread> #include <condition_variable> #include <mutex> using namespace std; class ThreadPool { using TaskType = std::function<void()>; public: ThreadPool(int n) : nthread(n) { works.reserve(n); for (int i = 0; i < n; ++i) { works.push_back(thread([this]() { while (true) { std::unique_lock<mutex> lk(mtx); cv.wait(lk, [this]() { return !tasks.empty() || stop; }); if (tasks.empty() && stop) { return; } TaskType task = std::move(tasks.front()); tasks.pop(); task(); } })); } } ~ThreadPool() { { std::unique_lock<mutex> lk(mtx); stop = true; } cv.notify_all(); for (int i = 0; i < nthread; ++i) { works[i].join(); } } void setStop(bool flag = true) { { std::unique_lock<mutex> lk(mtx); stop = true; } cv.notify_all(); } void addTask(TaskType task) { { std::unique_lock<mutex> lk(mtx); tasks.push(task); } cv.notify_one(); } private: int nthread; bool stop = false; // 默认是没退出 std::mutex mtx; std::condition_variable cv; std::vector<thread> works; std::queue<TaskType> tasks; }; int main() { ThreadPool pool(10); for (int i = 0; i < 100; ++i) { pool.addTask([x = i]() { // 注意这里变量的捕获需要用值捕获 cout << std::this_thread::get_id() << " 正在处理任务 " << x << endl; }); } return 0; } 

进阶版线程池

更加复杂的线程池:提交的任务函数类型是任意的,最终通过 std::bind 等封装为 std::function<void()> 类型

#include <vector> #include <thread> #include <iostream> #include <condition_variable> #include <mutex> #include <functional> #include <future> #include <queue> #include <memory> using namespace std; class ThreadPool{ using TaskType = std::function<void()>; private: std::vector<thread> pools; std::queue<TaskType> tasks; std::mutex mtx; std::condition_variable cv; bool flag = false; public: ThreadPool(int poolsize) { for (int i = 0; i < poolsize; ++i) { pools.push_back(thread([this]() { while (true) { std::function<void()> task; { std::unique_lock<std::mutex> lk(mtx); cv.wait(lk, [this]() { return !tasks.empty() || flag; }); if (tasks.empty() && flag) { return; } task = std::move(tasks.front()); tasks.pop(); } task(); } })); } } ~ThreadPool() { stop(); } template<typename F, class...Args> auto submit(F &&func, Args&& ...args) -> std::future<decltype(func(args...))> { using ReturnType = decltype(func(args...)); auto task = std::make_shared<std::packaged_task<ReturnType()>>( std::bind(std::forward<F>(func), std::forward<Args>(args)...) ); std::future<ReturnType> result = task->get_future(); { std::unique_lock<std::mutex> lk(mtx); tasks.emplace([task]() { (*task)(); }); } cv.notify_one(); return result; } void stop() { { std::unique_lock<std::mutex> lk(mtx); flag = true; } cv.notify_all(); for (int i = 0; i < pools.size(); ++i) { pools[i].join(); } } }; void exampleFunc(int n, int m) { cout << "thread = " << std::this_thread::get_id() << " n = " << n << " m = " << m << endl; } int main() { ThreadPool threadpool(3); std::vector<std::future<void>> results; for (int i = 0; i < 10; i++) { results.emplace_back(threadpool.submit(exampleFunc, i, i * 10)); } for (auto &result : results) { result.get(); } } 

大小端判断

大端序、小端序是什么?

  • 大端序:多字节数据的最高有效字节存放在最低的内存地址,而最低有效字节存放在最高的内存地址。例如:0x1234,其中 12 在第一个字节,34 在第二个字节。
  • 小端序:多字节数据的最低有效字节存放在最低的内存地址,而最高有效字节存放在最高的内存地址。例如:0x1234,其中 34 在第一个字节,12 在第二个字节
  • 大端序更加符合我们人类的读取习惯,如果地址从低往高来看的话,0x1234 就是按照我们从左往右的顺序读的,小端序更加符合计算机的习惯。

网络当中传输用的是什么字节序?

  • 大端序

为什么字节序重要?

字节序的概念对于跨平台的数据交换非常重要,特别是在网络通信和文件传输中。网络协议通常规定使用大端序(例如在以太网和 IP 协议中),而不同的计算机架构可能使用不同的字节序。因此,当数据在不同系统之间传输时,可能需要进行字节序转换(也称为字节序标准化或网络字节序转换)以确保数据的正确解释。

如何判断系统是大端序还是小端序

通过强制类型转换将整型低位的内容转为字符串,然后进行判断:

#include <stdio.h> int main() { unsigned int x = 0x; char *c = (char*)&x; if (*c == 0x78) { printf("系统是小端序\n"); } else { printf("系统是大端序\n"); } return 0; } 

或使用 C 语言联合体的性质:

#include <stdio.h> union { unsigned int u; unsigned char c[4]; } testend; int main() { testend.u = 0x; if (testend.c[0] == 0x12) { printf("大端序(Big-Endian)\n"); } else if (testend.c[0] == 0x78) { printf("小端序(Little-Endian)\n"); } else { printf("无法确定字节序\n"); } return 0; } 

智能指针的实现

简易版的智能指针实现如下:

unique_ptr 的实现

unique_ptr 比较简单,主要是几个方法要记住是干什么的,代码如下:

#include <iostream> using namespace std; template <typename T> class UniquePtr { private: T* ptr; // 内部指针 public: // 构造函数:接受一个指向 T 类型的指针 explicit UniquePtr(T* p = nullptr) : ptr(p) {} // 禁止拷贝构造和赋值操作 UniquePtr(const UniquePtr&) = delete; UniquePtr& operator=(const UniquePtr&) = delete; // 移动构造函数 UniquePtr(UniquePtr&& other) noexcept : ptr(other.ptr) { other.ptr = nullptr; // 将源指针置为 nullptr } // 移动赋值操作符 UniquePtr& operator=(UniquePtr&& other) noexcept { if (this != &other) { // 防止自我赋值 delete ptr; // 释放当前持有的资源 ptr = other.ptr; // 转移资源的所有权 other.ptr = nullptr; // 将源指针置为 nullptr } return *this; } // 重载解引用操作符 T& operator*() const { return *ptr; } // 重载箭头操作符 T* operator->() const { return ptr; } // 获取原始指针 T* get() const { return ptr; } // 释放所拥有的指针并返回它 T* release() { T* temp = ptr; ptr = nullptr; return temp; } // 重置指针,释放当前指针并拥有新的指针 void reset(T* p = nullptr) { if (ptr) { delete ptr; } ptr = p; } // 析构函数,自动释放内存 ~UniquePtr() { if (ptr) { delete ptr; } } }; // 测试 unique_ptr 实现 int main() { UniquePtr<int> p1(new int(42)); // 创建一个 unique_ptr std::cout << *p1 << std::endl; // 输出 42 UniquePtr<int> p2 = std::move(p1); // 移动所有权 if (p1.get() == nullptr) { std::cout << "p1 is now nullptr" << std::endl; } std::cout << *p2 << std::endl; // 输出 42 p2.reset(new int(100)); // 重置指针 std::cout << *p2 << std::endl; // 输出 100 return 0; } 

shared_ptr 的实现

核心是引用计数原理,注意细节部分:

  • 拷贝赋值和移动赋值的时候需要将原来的指针进行释放;
  • 移动赋值运算符参数不能加 const,返回值和拷贝赋值运算符一样是引用类型;
  • 原子变量获得值的方法是 load()
#include <iostream> #include <memory> #include <atomic> using namespace std; template<typename T> class SharedPtr { private: T* ptr; // 原始指针 std::atomic<int>* ref_count; // 引用计数 void release() { if (ref_count) { (*ref_count)--; if (*ref_count == 0) { delete ptr; delete ref_count; } } } public: SharedPtr() : ptr(nullptr), ref_count(nullptr) {} explicit SharedPtr(T *p) : ptr(p), ref_count(new std::atomic<int>(1)) {} ~SharedPtr() { release(); } SharedPtr(const SharedPtr& rhs) : ptr(rhs.ptr), ref_count(rhs.ref_count) { if (ref_count) { (*ref_count)++; } } SharedPtr(SharedPtr&& rhs) : ptr(rhs.ptr), ref_count(rhs.ref_count) { rhs.ptr = nullptr; rhs.ref_count = nullptr; } SharedPtr& operator=(const SharedPtr& rhs) { if (this != &rhs) { release(); // 释放当前指针 ptr = rhs.ptr; ref_count = rhs.ref_count; if (ref_count) { (*ref_count)++; } } return *this; } SharedPtr& operator=(SharedPtr&& rhs) { if (this != &rhs) { release(); ptr = rhs.ptr; ref_count = rhs.ref_count; rhs.ptr = nullptr; rhs.ref_count = nullptr; } return *this; } T& operator*() const { return *ptr; } T* operator->() const { return ptr; } int use_count() const { return ref_count ? ref_count->load() : 0; } }; template<typename T, class...Args> SharedPtr<T> makeShared(Args&&...args) { return SharedPtr<T>(new T(std::forward<Args>(args)...)); } class A { public: A(int a, int b) : ma(a), mb(b) {} void print() { cout << "a = " << ma << " b = " << mb << endl; } private: int ma, mb; }; // 测试 shared_ptr 实现 int main() { SharedPtr<int> p1(new int(42)); // 创建一个 shared_ptr std::cout << "p1 use_count: " << p1.use_count() << std::endl; // 输出引用计数 { SharedPtr<int> p2 = p1; // 拷贝构造 std::cout << "p1 use_count: " << p1.use_count() << std::endl; // 输出引用计数 std::cout << "p2 use_count: " << p2.use_count() << std::endl; // 输出引用计数 } // p2 离开作用域,引用计数减 1 std::cout << "p1 use_count: " << p1.use_count() << std::endl; // 输出引用计数 SharedPtr<int> p3 = std::move(p1); // 移动构造 std::cout << "p1 use_count: " << p1.use_count() << std::endl; // 输出引用计数 std::cout << "p3 use_count: " << p3.use_count() << std::endl; // 输出引用计数     SharedPtr<A> pa = makeShared<A>(1, 2);     pa->print();     return 0; } 

内存池的实现

这里只实现了一个简单版本的内存池,基本思路,开辟一块大的空间,然后通过嵌入式指针的手法将每个块的前 8 个字节作为指针,存放下一块的地址,通过空闲链表串联起来。

class MemoryPool { public: MemoryPool(size_t size, size_t count) : blocksize(size), blockcount(count) { memory = (char*)malloc(blocksize * blockcount); freelist = memory; // 初始化,freelist指向内存池第一块内容 char *curr_block = memory; for (size_t i = 0; i < count - 1; ++i) { // 将每个块的前8个字节作为指针,存放下一个块的地址 *(void)curr_block = (void*)(curr_block + blocksize); curr_block += blocksize; } *(void)curr_block = nullptr; } ~MemoryPool() { free(memory); } void* allocateBlock() { if (freelist == nullptr) { return nullptr; } void *block = freelist; freelist = *(void)block; // freelist指向下一个指针 return block; } void deallocate(void* ptr) { // 检查内存是否在内存池的范围之内 if (!(ptr >= memory && ptr < memory + blocksize * blockcount)) { return; } // 将块插入空闲链表的头部 *(void)ptr = freelist; freelist = ptr; } private: char *memory = nullptr; void *freelist = nullptr; size_t blocksize; size_t blockcount; }; int main() { { MemoryPool pool(sizeof(int*), 1000); int *p = (int*)pool.allocateBlock(); *p = 10; int *p2 = (int*)pool.allocateBlock(); *p2 = *p; std::cout << "&p = " << p << " *p = " << *p << std::endl; std::cout << "&p2 = " << p2 << " *p2 = " << *p2 << std::endl; } } 

线程安全的 HashMap

实现一个线程安全的 HashMap,这里采用的是开链法,整体不难实现,hash 函数那个可能要注意一下写法。这里没有进一步实现扩容操作了,如果需要实现整体会更加复杂。

#include <vector> #include <iostream> #include <list> #include <mutex> #include <thread> using namespace std; template<typename KeyType, typename ValueType> class HashMap { private: struct Bucket { std::list<std::pair<KeyType, ValueType>> ls; std::mutex mtx; }; std::vector<Bucket> table; size_t hashFunction(const KeyType &key) { return std::hash<KeyType>()(key) % table.size(); } public: HashMap(size_t size = 1001) : table(size) {} void insert(const KeyType& key, const ValueType& value) { size_t index = hashFunction(key); auto &bucket = table[index]; std::unique_lock<std::mutex> lk(bucket.mtx); for (auto &kv : bucket.ls) { if (kv.first == key) { kv.second = value; return; // 键存在 } } bucket.ls.emplace_back(key, value); // 键不存在 } bool get(const KeyType& key, ValueType& value) { size_t index = hashFunction(key); auto &bucket = table[index]; std::unique_lock<std::mutex> lk(bucket.mtx); for (auto &kv : bucket.ls) { if (kv.first == key) { value = kv.second; return true; // 键存在 } } return false; // 键不存在 } bool erase(const KeyType& key) { size_t index = hashFunction(key); auto &bucket = table[index]; std::unique_lock<std::mutex> lk(bucket.mtx); for (auto it = bucket.ls.begin(); it != bucket.ls.end(); ++it) { if (it->first == key) { bucket.ls.erase(it); return true; } } return false; // 键不存在 } }; int main() { HashMap<int, int> mp; for (int i = 0; i < 5; i++) { mp.insert(i, i * 10); } for (int i = 0; i < 5; i++) { int value = -1; if (mp.get(i, value)) { cout << i << " key find, value = " << value << endl; } else { cout << i << " key not find" << endl; } } for (int i = 0; i < 2; i++) { mp.erase(i); } cout << "==================" << endl; for (int i = 0; i < 5; i++) { int value = -1; if (mp.get(i, value)) { cout << i << " key find, value = " << value << endl; } else { cout << i << " key not find" << endl; } } } 

实现 string 类

问题:写一个简单的 String 类,包含构造函数,拷贝构造函数,移动构造函数,拷贝赋值函数,析构函数,通过通过指针初始化,在这个基础上实现一个 append 函数

实现思路:这个问题主要是想考查几种构造函数的写法,然后 append 的函数的话,考虑采用 STL vector 的思想,提前预留一定的空间,当添加的字符串长度超过这个空间的时候进行扩容,下面是我的代码实现,仅供参考

有一些注意点:

  • 1)采用 new 分配一个数组的时候,需要配套采用 delete[],析构函数当中需要释放内存;
  • 2)拷贝构造函数传递的参数是 const 引用类型,而移动构造函数不是引用类型;
  • 3)拷贝、移动赋值运算符需要进行特判,防止自己给自己赋值导致对象失效的问题;
#include <iostream> #include <string.h> using namespace std; class String { public: String() : str_(nullptr), size_(0), capacity_(10) {} String(const char *s) : capacity_(strlen(s) * 2), size_(strlen(s)) { str_ = new char[capacity_]; strcpy(str_, s); } ~String() { if (str_) { delete[] str_; } } // 深拷贝 String(const String &rhs) : size_(rhs.size_), capacity_(rhs.capacity_) { str_ = new char[rhs.size_]; strcpy(str_, rhs.str_); } String& operator=(const String &rhs) { if (this == &rhs) { return *this; } str_ = new char[rhs.size_]; strcpy(str_, rhs.str_); size_ = rhs.size_; capacity_ = rhs.capacity_; return *this; } String(String &&rhs) : str_(rhs.str_), size_(rhs.size_), capacity_(rhs.capacity_) { // 销毁rhs对象 rhs.str_ = nullptr; rhs.capacity_ = 0; rhs.size_ = 0; } String& operator=(String &&rhs) { if (this == &rhs) { return *this; } str_ = rhs.str_; capacity_ = rhs.capacity_; size_ = rhs.size_; // 销毁rhs对象 rhs.str_ = nullptr; rhs.capacity_ = 0; rhs.size_ = 0; return *this; } void append(const char *s) { int len = strlen(s); if (len + size_ < capacity_) { // 注意添加的字符串可能比扩容2倍后还要大,所以这里用循环判断一下 while (len + size_ < capacity_) { capacity_ *= 2; } char *newstr = new char[capacity_ * 2]; strcpy(newstr, str_); delete[] str_; // 删除旧的字符串 str_ = newstr; size_ += len; } else { strcpy(str_ + size_, s); size_ += len; } } int size() const { return size_; } int capacity() const { return capacity_; } void print() { if (str_) { cout << str_; } cout << endl; } private: int capacity_ = 0; int size_ = 0; char *str_; }; int main() { const char *s = "hello "; String str1(s); str1.print(); str1.append("world! "); str1.print(); str1.append("this is a massage"); str1.print(); std::cout << "===================" << std::endl; String tmp = "this is a string"; String str2 = tmp; // 测试拷贝构造函数 str2.print(); String str3; str3 = str2; // 测试拷贝赋值运算符 str3.print(); String str4 = std::move(str2); // 测试移动构造函数 str4.print(); cout << "str2 = "; str2.print(); // str2应该为空 cout << "test str5" << endl; String str5; str5 = std::move(str4); // 测试移动赋值运算符 str5.print(); return 0; } 

参考资料

  • 线程池代码(整合版)
  • 智能指针简单设计
  • C++面试之设计题
  • 单例模式 - 巴基速递
到此这篇c++练手_c语言编程题库的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • c++全排列问题_全排列怎么算2024-11-12 12:09:05
  • c++子集和问题_子集怎么表示2024-11-12 12:09:05
  • ai 编程_c++编程工具2024-11-12 12:09:05
  • c++格式化读取_C++程序设计2024-11-12 12:09:05
  • cpp 泛型_c++boost库2024-11-12 12:09:05
  • c++类和对象_类的定义和对象的创建与使用2024-11-12 12:09:05
  • C++学习笔记(二、C++基本概念)2024-11-12 12:09:05
  • linux c编程实战_好用的linux2024-11-12 12:09:05
  • 泛型程序设计与c++标准模板库_c++模板特化2024-11-12 12:09:05
  • c++格式化输出字符串_C++格式化输出2024-11-12 12:09:05
  • 全屏图片