C++ 模拟Go语言

只使用统一初始化

C++的初始化实在太多了,C++11 出现后,只推荐使用统一初始化。

class MyClass {
public:
    MyClass() { /*...*/ }
    MyClass(int x, double y) { /*...*/ }
};

MyClass obj1{};  // 使用统一初始化调用默认构造函数

MyClass obj2{1, 3.14};  // 使用统一初始化调用带参数的构造函数

MyClass obj3{obj2};  // 使用统一初始化调用拷贝构造函数
MyClass&& createObject() {
    MyClass temp{1, 3.14};
    return temp;
}
// 使用统一初始化调用移动构造函数
MyClass obj4{createObject()}; 

尽管统一初始化在很多情况下都是合适的,但在某些特定场景中,你可能需要使用其他初始化方法。例如,当使用某些老旧的API或与旧代码库交互时,可能需要用到传统的初始化方法(如圆括号或等号)。此外,对于某些高级用法,比如委托构造函数或显式调用拷贝构造函数,你可能仍然需要使用传统的初始化语法。

Go语言的Defer

难以释放的资源与RAII

void f() {
   int*p = new int{3};
   int error = doSomething(p);
   if (error)
       return;
   
   finalize(p);
   delete p;
}

一开始申请了内存,存储int,在最后函数结束的时候,通过delete p释放内存。上面的代码就有可能内存泄漏。因为假设doSomething 返回了错误,函数就会提前结束,而delete p就不会执行导致内存泄漏了!
修改代码

void f() {
   int*p = new int{3};
   int error = doSomething(p);
   if (error) {
       delete p; //释放内存,当出现错误的时候
       return;
    }
   
   finalize(p);
   delete p;
}

是不是添加了上面的错误处理,就不会有内存泄漏了?不!上面的代码可能还存在内存泄漏。如果doSomething抛出异常,那么两个delete p都不会被执行,内存泄漏!
有人可能会说,事儿怎么这么多,直接加上try catch,在catch的时候释放内存不就OK了嘛?实际上并不OK,因为加上try catch犯的是更严重的错误。

而且就算加了,代码还是可能会内存泄漏。因为哪天有程序员(可能是自己)增加新的代码的时候,就可能忘记delete了。

所以这种靠程序员细致认真的方法,是不靠谱的!

C++提供的解决方案是RAII,RAII 全称就Resource acquisition is initialization. 意为资源获取要通过构造函数初始化,然后析构函数负责释放资源。RAII替我们实现了”某个操作在任何分支结束的时候,会被执行,且被执行一次“。

C++ 版本的Defer

在C++ 中叫做Scope Guard,配合lambda一起使用。
ScopeGuard保证函数结束时,不论怎么退出,都会delete当前的指针。ScopeGuard一般用于关闭资源。

#include<functional>
#include<iostream>

class ScopeGuard{
    std::function<void()> mFunc;
public:
    ScopeGuard(std::function<void()> f)  {
        mFunc = f;
    }
    ~ScopeGuard()  {
        mFunc();
    }
};

int doSomething(int* p);
void finalize(int* p);

void f() {
   int* p = new int{3};
   ScopeGuard scopeGuard ([&p]() {  //这里使用了ScopeGuard
        if (p)
           delete p;
        std::cout << "delete point\n";
    });
   int error = doSomething(p);
   if (error) 
       return;  
   finalize(p);
   std::cout<<"Function ends!\n";
}

int main(){
    f();
}

int doSomething(int* p) {
   return -1;
}
void finalize(int* p) {
}

C++ 标准库没有提供类似ScopeGuard的类,我们可以通过std::unique_ptr实现类似的行为。

void f()
{
    int* p = new int{ 1 };
    auto deferFun = [&p](void*) {
        delete p;
    };
    std::unique_ptr<void, decltype(deferFun)> ptr1{ (void*)1, deferFun };
}
int main()
{
    f();
    return 0;
}

Go 语言的Channel

我们用生产者和消费者解释这个模型。
生产者向管道的一端放数据,消费者在管道另一端接收数据。

  1. 如果没有数据,消费者应该被阻塞。
  2. 如果有数据,应该通知消费者获取。
  • 对于没有数据的情况,我们应该调用条件变量的wait方法,让消费者处于阻塞状态。
  • 生产者放数据的时候,应该唤醒消费者,调用条件变量的notify_one方法。通知消费者获取
  • 生产结束的时候,应该notify_all,唤醒所有阻塞的消费者。
///Go like Channel Implementation
template<class item>
class Channel {
private:
    std::list<item> queue;
    std::mutex m;
    std::condition_variable cv;
    bool closed;
public:
    Channel() : closed(false) {}

    void close() {
        std::unique_lock<std::mutex> lock(m);
        closed = true;
        cv.notify_all();
    }

    bool is_closed() {
        std::unique_lock<std::mutex> lock(m);
        return closed;
    }

    void put(const item &i) {
        std::unique_lock<std::mutex> lock(m);
        if (closed)
            throw std::logic_error("put to closed channel");
        queue.push_back(i);
        cv.notify_one();
    }

    bool get(item &out, bool wait = true) {
        std::unique_lock<std::mutex> lock(m);
        if (wait)
        {
//这里为了防止虚假唤醒,添加了一个匿名函数
//当被虚假唤醒,队列为空,还会继续等待
            cv.wait(lock, [&]() { return closed || !queue.empty(); });
        }
        if (queue.empty())
            return false;
        out = queue.front();
        queue.pop_front();
        return true;
    }
};
Channel<bool> touchChannel;
void eventReaderThread(int fd) {
  while(true)
  {
     touchChannel.put(true);
     sleep(500);
  }
}
void eventWriterThread(){
  bool sync;
  while(touchChannel.get(sync)){
     //do something
  }
}

把参数类型放到函数后面

C++ 的函数尾返回类型

auto function(int param) -> double; // 尾返回类型
auto function(int param) -> double; // 尾返回类型
//返回一个函数指针, 这个函数指针参数为空,返回类型为函数指针,该函数返回int
auto pf1()->auto (*)()->int(*)()
{
}
//如果不用追踪返回类型的写法, 绕来绕去~
int (*(*pdf())())()
{
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李来群

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值