别的代码也比较多,源码很好玩

我们继续看uvw的源码,本次看的东西比较多,去除底层的一对事物,很多代码都以贯穿的,耦合度也正如高了。紧要回顾上面多少个文本的代码:

UVW源码漫谈(三),uvw源码漫谈

咱俩继续看uvw的源码,这一次看的事物相比较多,去除底层的有个别东西,很多代码都以贯通的,耦合度也比较高了。紧要包涵下边多少个文件的代码:

underlying_type.hpp  resource.hpp  loop.hpp  handle.hpp  stream.hpp  tcp.hpp

代码小编就不都贴出了,说到何处贴哪里的代码。假使有心情可以打开源码对照看看。别的代码也比较多,小编先大约分析下源码的构造,再说有的细节的和体系基本非亲非故的东西。

 

源码很好玩

① 、保存本身的share_ptr——通过这些题目来通览一下源码。

在第叁篇给大家介绍uvw用法的时候,不通晓大家有没有留意到(算了,肯定没留神),小编把大约的代码贴出来给大家看一下:

 1 void listen(uvw::Loop &loop) {
 2     std::shared_ptr<uvw::TcpHandle> tcp = loop.resource<uvw::TcpHandle>();
 3 
 4 。。。。。。
 5 
 6 }
 7 
 8 void conn(uvw::Loop &loop) {
 9     auto tcp = loop.resource<uvw::TcpHandle>();
10 。。。。。。
11 
12 }
13 
14 void g() {
15     auto loop = uvw::Loop::getDefault();
16     listen(*loop);
17     conn(*loop);
18     loop->run();
19     loop = nullptr;
20 }
21 
22 int main() {
23     g();
24 }

那时候listen和conn函数中,都有2个tcp变量,但是这么些变量在函数内部,按道理说,遵照g()中的顺序走下来,那七个部分变量应该早已经被自动销毁了,可是怎么还是能再回调到事件处理函数?有的看官大概会疑忌,是否她们都封存在loop中,其实自个儿一伊始也如此认为,终归是调用loop的resource方法创建的。这就先看看resource的代码:

源码1  loop.hpp  266-270

1     template<typename R, typename... Args>
2     std::enable_if_t<not std::is_base_of<BaseHandle, R>::value, std::shared_ptr<R>>
3     resource(Args&&... args) {
4         return R::create(shared_from_this(), std::forward<Args>(args)...);
5     }

咱俩把TcpHandle的模板参数带进去看,哦,看来那边是调用的TcpHandle::create()这几个静态函数,难道那就认证了loop没有保存TcpHandle?
可是create中显著传入了shared_from_this()参数,于是不死心,继续找create的落成,话说这么多类拐来拐去的,着实找了一阵子,终于找到了:

源码2  underlying_type.hpp  76-79

1     template<typename... Args>
2     static std::shared_ptr<T> create(Args&&... args) {
3         return std::make_shared<T>(ConstructorAccess{0}, std::forward<Args>(args)...);
4     }

看到此间,作者就懵比了,传进来的 std::shared_ptr<Loop> 难道是跟
args 一起,被 std::forward
给吃了?好啊,那既然是要创造1个 std::shared_ptr<TcpHandle>,而且还传入了一堆参数,肯定是有TcpHandle的构造函数的吧。于是作者把TcpHandle类的八辈儿祖宗都找了一回,终于照旧在underlying_type.hpp中找到了:

源码3  underlying_type.hpp  57-59

1     explicit UnderlyingType(ConstructorAccess, std::shared_ptr<Loop> ref) noexcept
2         : pLoop{std::move(ref)}, resource{}
3     {}

话说那一个构造函数里也什么都没干,只是把loop保存了一晃哟。那上边的难题怎么解释。于是本人又看了一回,原来Loop::resource还有八个落到实处:

源码4  loop.hpp  248-254

1     template<typename R, typename... Args>
2     std::enable_if_t<std::is_base_of<BaseHandle, R>::value, std::shared_ptr<R>>
3     resource(Args&&... args) {
4         auto ptr = R::create(shared_from_this(), std::forward<Args>(args)...);
5         ptr = ptr->init() ? ptr : nullptr;
6         return ptr;
7     }

那么些落成与源码1
长的专门像,那在底下会说到。来探视这么些达成里面。果然,多调用了3个init,而以此init貌似是TcpHandler的分子函数,来探望init里面有怎么着东西,

源码5  tcp.hpp  62-66

1     bool init() {
2         return (tag == FLAGS)
3                 ? initialize(&uv_tcp_init_ex, flags)
4                 : initialize(&uv_tcp_init);
5     }

源码6  handle.hpp  45-58

 1     template<typename F, typename... Args>
 2     bool initialize(F &&f, Args&&... args) {
 3         if(!this->self()) {
 4             auto err = std::forward<F>(f)(this->parent(), this->get(), std::forward<Args>(args)...);
 5 
 6             if(err) {
 7                 this->publish(ErrorEvent{err});
 8             } else {
 9                 this->leak();
10             }
11         }
12 
13         return this->self();
14     }

这其间其实就是用 uv_tcp_init
来做了一下初阶化,可以看出第伍行,this->parent就是loop指针,this->get就是uv_tcp_t,小编就不贴代码了,大家从源码里翻看一下。那里借使初阶化成功是早晚会调用leak的,继续往下看

源码7  resource.hpp  27-29

1     void leak() noexcept {
2         sPtr = this->shared_from_this();
3     }

此间就3个功力,把 this->shared_from_this()
赋给了和谐的积极分子变量。难道那就是题材的关键所在?

 

作者不服,怎么只怕有那种操作,于是本人做了个试验,代码如下:

 1 #include <iostream>
 2 #include <thread>
 3 #include <mutex>
 4 #include <condition_variable>
 5 
 6 std::mutex g_mutex;
 7 std::condition_variable g_cond;
 8 
 9 class C : public std::enable_shared_from_this<C> {
10 public:
11     C() {
12         std::cout << "C" << std::endl;
13         msg = "Hello World";
14     }
15 
16     ~C() {
17         std::cout << "~C" << std::endl;
18     }
19 
20     void init() {
21         local = this->shared_from_this();
22     }
23 
24     void thread_fun() {
25         while(true) {
26             std::unique_lock<std::mutex> lk(g_mutex);
27             g_cond.wait(lk);
28             std::cout << msg << std::endl;
29         }
30     }
31 
32     void print() {
33         th = std::thread(&C::thread_fun, this);
34         th.detach();
35     }
36 
37 private:
38     std::shared_ptr<C> local;
39     std::thread th;
40     std::string msg;
41 };
42 
43 void fun() {
44     shared_ptr<C> c = std::make_shared<C>();
45     c->init();
46     c->print();
47 }
48 
49 
50 int main(int argc, char* argv[])
51 {
52     fun();
53     std::cout << "fun finish" << std::endl;
54     std::this_thread::sleep_for(std::chrono::milliseconds(1000));
55 
56     for(int i = 0; i < 4; i++) {
57         g_cond.notify_all();
58         std::this_thread::sleep_for(std::chrono::milliseconds(1000));
59     }
60 
61     return 0;
62 }

以此测试代码基本是模仿了源码中的景况,用线程加条件变量来模拟信号的发出,输出结果如下:

1 C
2 fun finish
3 Hello World
4 Hello World
5 Hello World
6 Hello World

万一把上边代码的第陆5行注释掉,输出结果:

1 C
2 ~C
3 fun finish
4 
5 
6 
7

留神,下边4行是打印出来的,比较一下那三个运维结果,第叁种情状在fun为止在此之前是绝非调用C类析构函数的,直到程序运转停止。而第两种情状在fun停止以前就调用了析构函数。那还不够,即使两种意况中线程一贯都在运维,可是第2种没有打印出“Hello
World”,越发证实了地点的如若。

下一场就是怎么来分解那种情状,作者把fun函数改造一下:

1 void fun() {
2     shared_ptr<C> c = std::make_shared<C>();
3     std::cout << "use count: " << c.use_count() << std::endl;
4     c->init();
5     std::cout << "use count: " << c.use_count() << std::endl;
6     c->print();
7 }

世家可以试一下,第两次打印,引用计数是1,第贰遍打印,引用计数是2。当fun为止时,c被灭绝,引用计数-1,还剩余1,保存在类中的local中,所以还不恐怕释放内存。只怕说,在fun中结构的c,永远都不会自由,直到程序甘休,程序所用的内存会由操作系统自动回收。

可能有点人见过那种用法,不过作者真正是第3次相见。不管各位看官感觉如何,反正我是觉得小编棒棒哒,几乎就是多个头脑boy,KeKe~~

观看此间,还有3个标题,为啥小编不把Handle保存在Loop中,而要以那种措施来拍卖呢?其实大家得以在Loop中声美素佳儿个

1     std::vector<std::shared_ptr<void>> handles;

这么不就可以保存Handle了,或者我还有其余的考虑,大家之后再看。

 

② 、代码的结构

骨子里若是有看官跟着下面的手续走一下,基本上应当是把这一个项目标多数事物都打听了弹指间,项目的差不离的继承关系也会比较清楚了,其余的骨子里就是一些对libuv东西的卷入和运用,有趣味把源码来回翻看一下。作者相信对于接触c++时间较短只怕对c++11,14规范比较生疏的会收益匪浅。同时,借使您是libuv的使用者,你大概会从里面学到一些此外的应用格局。

如拾草芥同学会自身看一些品类的源代码,然则不少人看二分一,大概看一丢丢对友好有用的,就放下了。对于我们程序员来说,看质量好的源代码是十一分主要的,大家得以从中了然我的考虑,小编消除难点的思路和方法,小编每行代码的谋划,以及项目标宏图和规划,还有别的许多广大东西,固然再不行,大家也得以借鉴人家的代码,举办改动,那也是一种学习格局。

说了那般多废话,就3个情趣,很多事物本身写出来,一是抒发糟糕,二是我们看了也是三只雾水,所以有趣味的或然看源码来的根本。

来看一下那边代码结构和延续关系是怎么的:

图片 1

 那是从代码生成的docxgen文档中截的,文档下载链接:https://files.cnblogs.com/files/yxfangcs/uvw\_html.zip

 

一些C++的东东

1、std::unique_ptr

上次有跟我们提到过好几智能指针的东东,给了二个链接回看一下的。不过多少东西没说到,明天联合看一下。先看代码:

源码8  loop.hpp  184-202

 1     static std::shared_ptr<Loop> getDefault() {
 2         static std::weak_ptr<Loop> ref;
 3         std::shared_ptr<Loop> loop;
 4 
 5         if(ref.expired()) {
 6             auto def = uv_default_loop();
 7 
 8             if(def) {
 9                 auto ptr = std::unique_ptr<uv_loop_t, Deleter>(def, [](uv_loop_t *){});
10                 loop = std::shared_ptr<Loop>{new Loop{std::move(ptr)}};
11             }
12 
13             ref = loop;
14         } else {
15             loop = ref.lock();
16         }
17 
18         return loop;
19     }

且先不看那一个函数是干嘛的,看到第玖行。大家例行用std::unique_ptr基本就是这般的:

1 std::unique_ptr<uv_loop_t> ptr = std::make_unique<uv_loop_t>();

接下来大家也明白,unique_ptr要用move来传递,大家也领略,这一个智能指针会在距离功能域的时候自动释放。像第8行如此的用法大家恐怕就很少见到了,先来探望unique_ptr的原形:

1 template<
2     class T,
3     class Deleter = std::default_delete<T>
4 > class unique_ptr;
5 
6 template <
7     class T,
8     class Deleter
9 > class unique_ptr<T[], Deleter>;

啊,这下就精通了,原来是有这么个东西存在的,那里的模板变量T就是我们例行传入的门类,而Deleter是有三个私行认同值的,std::default_delete<T>
基本上就似乎于delete了,那里大家也是可以自定义的,像下面用法中的Deleter大家得以在代码中找到:

源码9  loop.hpp  143

1     using Deleter = void(*)(uv_loop_t *);

本条using的用法在事先的博客中有写过。在那些用法中,我们得以自动定义unique_ptr的结构和销毁的操作,看上面的例子:

1 std::unique_ptr<std::FILE, decltype(&std::fclose)> fp(std::fopen("demo.txt", "r"), &std::fclose);
3 if(fp)
4   std::cout << (char)std::fgetc(fp.get()) << '\n';

(那段出自:http://en.cppreference.com/w/cpp/memory/unique\_ptr 有趣味能够点开看看)

怎样,那样用是否专程舒服。在相距fp的效能域后,unique_ptr会自动调用fclose来关闭文件。这里面有二个decltype,这几个事物其实就是来回到参数的品类的,比如上边我不清楚fclose的花果山真面目是什么,那么自身得以一贯用decltype来回到它的花色。举个例子:

1 auto fun1 = [](int a){return a;};
2 decltype(fun1) fun2 = fun1;

再观望源码1的第八行,那儿用{}来开端化,在事先博客中也说到过,叫列表初步化,上边打开文件的例子也足以如此写:

1 std::unique_ptr<std::FILE, decltype(&std::fclose)> fp{std::fopen("demo.txt", "r"), &std::fclose};
2 if(fp)
3    std::cout << (char)std::fgetc(fp.get()) << '\n';

也是没什么的。

 

2、std::enable_if_t

把地点代码再贴一下,方便看

源码10  loop.hpp  248-254

1     template<typename R, typename... Args>
2     std::enable_if_t<std::is_base_of<BaseHandle, R>::value, std::shared_ptr<R>>
3     resource(Args&&... args) {
4         auto ptr = R::create(shared_from_this(), std::forward<Args>(args)...);
5         ptr = ptr->init() ? ptr : nullptr;
6         return ptr;
7     }

这边的enable_if_t的原型是:

1 template<bool B, class T = void>
2 struct enable_if;
3 
4 template< bool B, class T = void >
5 using enable_if_t = typename enable_if<B,T>::type;

enable_if 的重大成效就是当某些
成立时,enable_if可以提供某连串型。可是当
不满意的时候,enable_if<>::type 就是未定义的,当用到模板相关的景色时,只会实例化失利,不会编译错误。

对此地点的例证,意思就是,若是Lacrosse的基类是BaseHandle,那重回的品种就是std::share_ptr<奥迪Q3>,否则重回的品类是未定义的,约等于说resource函数模板会实例化败北,程序运营错误。具体可以看:http://en.cppreference.com/w/cpp/types/enable\_if

那即使实例化战败那程序不就挂了,所以笔者又给了上面的一段完毕:

源码11  loop.hpp  266-270

1 template<typename R, typename... Args>
2     std::enable_if_t<not std::is_base_of<BaseHandle, R>::value, std::shared_ptr<R>>
3     resource(Args&&... args) {
4         return R::create(shared_from_this(), std::forward<Args>(args)...);
5     }

情趣就是借使LX570的基类不是BaseHandle就用那么些函数模板,那几个函数模板里就平素不源码10中的对init的调用,可知小编如故考虑的百般详尽的。

 

下一篇

下一篇就来看一下档次中其它文件中的一些事物,看看有没怎么好玩的牵线给我们,或然再写个一两篇就可以了结了。文中有不当或有可改进之处,希望我们不吝赐教,谢谢。

http://www.bkjia.com/cjjc/1228100.htmlwww.bkjia.comtruehttp://www.bkjia.com/cjjc/1228100.htmlTechArticleUVW源码漫谈(三),uvw源码漫谈
大家继续看uvw的源码,本次看的事物比较多,去除底层的某个东西,很多代码都以贯通的,耦合度也相比高…

underlying_type.hpp  resource.hpp  loop.hpp  handle.hpp  stream.hpp  tcp.hpp

代码作者就不都贴出了,说到哪个地方贴什么地方的代码。若是有来头可以打开源码对照看看。其余代码也正如多,我先大致分析下源码的社团,再说某个细节的和花色为主无关的事物。

 

源码很好玩

① 、保存自身的share_ptr——通过那么些难题来通览一下源码。

在首先篇给大家介绍uvw用法的时候,不明了大家有没有在意到(算了,肯定没注意),小编把大致的代码贴出来给大家看一下:

 1 void listen(uvw::Loop &loop) {
 2     std::shared_ptr<uvw::TcpHandle> tcp = loop.resource<uvw::TcpHandle>();
 3 
 4 。。。。。。
 5 
 6 }
 7 
 8 void conn(uvw::Loop &loop) {
 9     auto tcp = loop.resource<uvw::TcpHandle>();
10 。。。。。。
11 
12 }
13 
14 void g() {
15     auto loop = uvw::Loop::getDefault();
16     listen(*loop);
17     conn(*loop);
18     loop->run();
19     loop = nullptr;
20 }
21 
22 int main() {
23     g();
24 }

那时候listen和conn函数中,都有三个tcp变量,不过这一个变量在函数内部,按道理说,依据g()中的顺序走下来,那多个部分变量应该早已经被自动销毁了,不过怎么仍能再回调到事件处理函数?有的看官只怕会疑心,是还是不是她们都封存在loop中,其实本人一开端也如此觉得,毕竟是调用loop的resource方法成立的。那就先看看resource的代码:

源码1  loop.hpp  266-270

1     template<typename R, typename... Args>
2     std::enable_if_t<not std::is_base_of<BaseHandle, R>::value, std::shared_ptr<R>>
3     resource(Args&&... args) {
4         return R::create(shared_from_this(), std::forward<Args>(args)...);
5     }

咱俩把TcpHandle的沙盘参数带进去看,哦,看来那边是调用的TcpHandle::create()这么些静态函数,难道那就注脚了loop没有保存TcpHandle?
可是create中鲜明传入了shared_from_this()参数,于是不死心,继续找create的完成,话说这么多类拐来拐去的,着实找了片刻,终于找到了:

源码2  underlying_type.hpp  76-79

1     template<typename... Args>
2     static std::shared_ptr<T> create(Args&&... args) {
3         return std::make_shared<T>(ConstructorAccess{0}, std::forward<Args>(args)...);
4     }

看到此间,作者就懵比了,传进来的 std::shared_ptr<Loop> 难道是跟
args 一起,被 std::forward
给吃了?好啊,那既然是要创建3个 std::shared_ptr<TcpHandle>,而且还传入了一堆参数,肯定是有TcpHandle的构造函数的呢。于是作者把TcpHandle类的八辈儿祖宗都找了一次,终于如故在underlying_type.hpp中找到了:

源码3  underlying_type.hpp  57-59

1     explicit UnderlyingType(ConstructorAccess, std::shared_ptr<Loop> ref) noexcept
2         : pLoop{std::move(ref)}, resource{}
3     {}

话说这些构造函数里也什么都没干,只是把loop保存了一下啊。那上面的题材怎么解释。于是自个儿又看了五回,原来Loop::resource还有一个贯彻:

源码4  loop.hpp  248-254

1     template<typename R, typename... Args>
2     std::enable_if_t<std::is_base_of<BaseHandle, R>::value, std::shared_ptr<R>>
3     resource(Args&&... args) {
4         auto ptr = R::create(shared_from_this(), std::forward<Args>(args)...);
5         ptr = ptr->init() ? ptr : nullptr;
6         return ptr;
7     }

那么些达成与源码1
长的专门像,那在底下会说到。来探视这一个完结里面。果然,多调用了三个init,而以此init貌似是TcpHandler的成员函数,来探望init里面有怎么着事物,

源码5  tcp.hpp  62-66

1     bool init() {
2         return (tag == FLAGS)
3                 ? initialize(&uv_tcp_init_ex, flags)
4                 : initialize(&uv_tcp_init);
5     }

源码6  handle.hpp  45-58

 1     template<typename F, typename... Args>
 2     bool initialize(F &&f, Args&&... args) {
 3         if(!this->self()) {
 4             auto err = std::forward<F>(f)(this->parent(), this->get(), std::forward<Args>(args)...);
 5 
 6             if(err) {
 7                 this->publish(ErrorEvent{err});
 8             } else {
 9                 this->leak();
10             }
11         }
12 
13         return this->self();
14     }

那中间其实就是用 uv_tcp_init
来做了一下伊始化,可以见见第⑥行,this->parent就是loop指针,this->get就是uv_tcp_t,小编就不贴代码了,我们从源码里翻看一下。这里倘诺开首化成功是早晚会调用leak的,继续往下看

源码7  resource.hpp  27-29

1     void leak() noexcept {
2         sPtr = this->shared_from_this();
3     }

此处就三个功力,把 this->shared_from_this()
赋给了投机的积极分子变量。难道这就是题材的关键所在?

 

自身不服,怎么大概有那种操作,于是本身做了个实验,代码如下:

 1 #include <iostream>
 2 #include <thread>
 3 #include <mutex>
 4 #include <condition_variable>
 5 
 6 std::mutex g_mutex;
 7 std::condition_variable g_cond;
 8 
 9 class C : public std::enable_shared_from_this<C> {
10 public:
11     C() {
12         std::cout << "C" << std::endl;
13         msg = "Hello World";
14     }
15 
16     ~C() {
17         std::cout << "~C" << std::endl;
18     }
19 
20     void init() {
21         local = this->shared_from_this();
22     }
23 
24     void thread_fun() {
25         while(true) {
26             std::unique_lock<std::mutex> lk(g_mutex);
27             g_cond.wait(lk);
28             std::cout << msg << std::endl;
29         }
30     }
31 
32     void print() {
33         th = std::thread(&C::thread_fun, this);
34         th.detach();
35     }
36 
37 private:
38     std::shared_ptr<C> local;
39     std::thread th;
40     std::string msg;
41 };
42 
43 void fun() {
44     shared_ptr<C> c = std::make_shared<C>();
45     c->init();
46     c->print();
47 }
48 
49 
50 int main(int argc, char* argv[])
51 {
52     fun();
53     std::cout << "fun finish" << std::endl;
54     std::this_thread::sleep_for(std::chrono::milliseconds(1000));
55 
56     for(int i = 0; i < 4; i++) {
57         g_cond.notify_all();
58         std::this_thread::sleep_for(std::chrono::milliseconds(1000));
59     }
60 
61     return 0;
62 }

这些测试代码基本是人云亦云了源码中的情形,用线程加条件变量来模拟信号的发生,输出结果如下:

1 C
2 fun finish
3 Hello World
4 Hello World
5 Hello World
6 Hello World

即使把下边代码的第六5行注释掉,输出结果:

1 C
2 ~C
3 fun finish
4 
5 
6 
7

留神,下边4行是打印出来的,相比一下那多个运营结果,第贰种情景在fun截止此前是绝非调用C类析构函数的,直到程序运行停止。而第一种景况在fun甘休此前就调用了析构函数。那还不够,即便二种境况中线程平素都在运营,不过第3种没有打印出“Hello
World”,特别证实了地方的如若。

下一场就是怎么来分解那种意况,我把fun函数改造一下:

1 void fun() {
2     shared_ptr<C> c = std::make_shared<C>();
3     std::cout << "use count: " << c.use_count() << std::endl;
4     c->init();
5     std::cout << "use count: " << c.use_count() << std::endl;
6     c->print();
7 }

世家可以试一下,第①遍打印,引用计数是1,第二回打印,引用计数是2。当fun截止时,c被销毁,引用计数-1,还剩余1,保存在类中的local中,所以还不可见自由内存。或然说,在fun中协会的c,永远都不会自由,直到程序停止,程序所用的内存会由操作系统自动回收。

唯恐有个别人见过那种用法,但是自个儿实在是首先次相遇。不管各位看官感觉什么,反正小编是认为作者棒棒哒,几乎就是三个脑筋boy,KeKe~~

看到此间,还有2个难题,为何小编不把Handle保存在Loop中,而要以那种格局来处理吧?其实我们得以在Loop中宣称壹个

1     std::vector<std::shared_ptr<void>> handles;

如此那般不就可以保存Handle了,只怕小编还有别的的考虑,大家随后再看。

 

② 、代码的社团

其实如若有看官跟着下边的步调走一下,基本上应当是把那一个类型的绝超过一半事物都询问了弹指间,项目的光景的三番一回关系也会相比较清楚了,其他的其实就是部分对libuv东西的包装和行使,有趣味把源码来回翻看一下。作者深信不疑对于接触c++时间较短只怕对c++11,14业内相比生疏的会收益匪浅。同时,假设你是libuv的使用者,你可能会从中间学到一些其余的施用方式。

有的是同学会本身看有的档次的源代码,不过众三人看百分之五十,只怕看一丢丢对友好有用的,就放下了。对于大家程序员来说,看质量好的源代码是十一分关键的,大家得以从中了解我的合计,小编消除难题的思绪和办法,小编每行代码的谋划,以及项目标布置和规划,还有任何很多众多东西,固然再尤其,我们也得以借鉴人家的代码,举办改动,那也是一种学习方式。

说了那样多废话,就三个情趣,很多东西小编写出来,一是发挥不佳,二是豪门看了也是3只雾水,所以有趣味的要么看源码来的彻底。

来看一下那边代码结构和持续关系是怎么的:

图片 2

 这是从代码生成的docxgen文档中截的,文档下载链接:https://files.cnblogs.com/files/yxfangcs/uvw_html.zip

 

一些C++的东东

1、std::unique_ptr

上次有跟大家提到过好几智能指针的东东,给了三个链接回想一下的。然则多少东西没说到,今日伙同看一下。先看代码:

源码8  loop.hpp  184-202

 1     static std::shared_ptr<Loop> getDefault() {
 2         static std::weak_ptr<Loop> ref;
 3         std::shared_ptr<Loop> loop;
 4 
 5         if(ref.expired()) {
 6             auto def = uv_default_loop();
 7 
 8             if(def) {
 9                 auto ptr = std::unique_ptr<uv_loop_t, Deleter>(def, [](uv_loop_t *){});
10                 loop = std::shared_ptr<Loop>{new Loop{std::move(ptr)}};
11             }
12 
13             ref = loop;
14         } else {
15             loop = ref.lock();
16         }
17 
18         return loop;
19     }

且先不看这一个函数是干嘛的,看到第10行。大家如常用std::unique_ptr基本就是这样的:

1 std::unique_ptr<uv_loop_t> ptr = std::make_unique<uv_loop_t>();

下一场我们也亮堂,unique_ptr要用move来传递,我们也了解,那些智能指针会在离开效用域的时候自动释放。像第7行如此的用法我们兴许就很少看到了,先来探视unique_ptr的原形:

1 template<
2     class T,
3     class Deleter = std::default_delete<T>
4 > class unique_ptr;
5 
6 template <
7     class T,
8     class Deleter
9 > class unique_ptr<T[], Deleter>;

嗯,这下就明白了,原来是有那般个东西存在的,那里的模版变量T就是我们如常传入的类型,而Deleter是有二个暗中同意值的,std::default_delete<T>
基本上就如于delete了,那里我们也是可以自定义的,像下面用法中的Deleter大家得以在代码中找到:

源码9  loop.hpp  143

1     using Deleter = void(*)(uv_loop_t *);

其一using的用法在后边的博客中有写过。在那么些用法中,我们得以自行定义unique_ptr的社团和销毁的操作,看上面的事例:

1 std::unique_ptr<std::FILE, decltype(&std::fclose)> fp(std::fopen("demo.txt", "r"), &std::fclose);
3 if(fp)
4   std::cout << (char)std::fgetc(fp.get()) << '\n';

(那段出自:http://en.cppreference.com/w/cpp/memory/unique_ptr 有趣味能够点开看看)

何以,那样用是否特意舒服。在距离fp的功效域后,unique_ptr会自动调用fclose来关闭文件。那些中有二个decltype,这么些事物其实就是来回到参数的档次的,比如上边小编不清楚fclose的真相是如何,那么本身得以一向用decltype来回到它的系列。举个例子:

1 auto fun1 = [](int a){return a;};
2 decltype(fun1) fun2 = fun1;

再观望源码1的第9行,那儿用{}来开头化,在此前博客中也说到过,叫列表开端化,上面打开文件的事例也可以那样写:

1 std::unique_ptr<std::FILE, decltype(&std::fclose)> fp{std::fopen("demo.txt", "r"), &std::fclose};
2 if(fp)
3    std::cout << (char)std::fgetc(fp.get()) << '\n';

也是没什么的。

 

2、std::enable_if_t

把上面代码再贴一下,方便看

源码10  loop.hpp  248-254

1     template<typename R, typename... Args>
2     std::enable_if_t<std::is_base_of<BaseHandle, R>::value, std::shared_ptr<R>>
3     resource(Args&&... args) {
4         auto ptr = R::create(shared_from_this(), std::forward<Args>(args)...);
5         ptr = ptr->init() ? ptr : nullptr;
6         return ptr;
7     }

这边的enable_if_t的原型是:

1 template<bool B, class T = void>
2 struct enable_if;
3 
4 template< bool B, class T = void >
5 using enable_if_t = typename enable_if<B,T>::type;

enable_if 的机要作用就是当有个别
成立时,enable_if能够提供某连串型。不过当
不满足的时候,enable_if<>::type 就是未定义的,当用到模板相关的场景时,只会实例化战败,不会编译错误。

对于地点的事例,意思就是,假设Tiggo的基类是BaseHandle,那再次回到的门类就是std::share_ptr<途睿欧>,否则重回的系列是未定义的,相当于说resource函数模板会实例化战败,程序运转错误。具体可以看:http://en.cppreference.com/w/cpp/types/enable_if

那如若实例化战败那程序不就挂了,所以作者又给了上面的一段已毕:

源码11  loop.hpp  266-270

1 template<typename R, typename... Args>
2     std::enable_if_t<not std::is_base_of<BaseHandle, R>::value, std::shared_ptr<R>>
3     resource(Args&&... args) {
4         return R::create(shared_from_this(), std::forward<Args>(args)...);
5     }

趣味就是如若Lacrosse的基类不是BaseHandle就用那一个函数模板,那些函数模板里就没有源码10中的对init的调用,可知作者如故考虑的不行详尽的。

 

下一篇

下一篇就来看一下项目中任何文件中的一些事物,看看有没怎么好玩的牵线给大家,或许再写个一两篇就可以了结了。文中有不当或有可改进之处,希望大家不吝赐教,谢谢。

相关文章