second_week

总结

一,CMake知识

1. 使用理由

  1. CMake靠的是CMakeLists.txt文件来生成工程的,事实上,CMakeLists.txt的编写就如使用make时编写Makefile,只不过,相对来说CMake站的高度更高一些,所以虽然还是要编写一个配置文件,但是CMakefile的编写比makefile轻松简单很多,而CMake最后其实还是通过生成makefile的方式来管理工程。
  2. 它可以让程序员通过一个与开发平台无关的CMakeLists.txt文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的Makefile和工程文件,如unix平台的Makefile或者win平台下面的VS工程。也是一种“Write once, run everywhere。

2. 使用方法

git链接
博客链接1
博客链接2
博客链接3

3. 存在问题

  1. 部分环境变量尚未理解
  2. 大型项目的CMakeLists.txt与complish脚本嵌套使用。

二,代码阅读

1. 疑难点

疑难点链接
包括c++新特性,CMake,Linux等。

2. 知识点

C++新特性链接
Docker链接
CMake链接
Cassandra链接
接着对常用的新特性和比较重要的点再做梳理

  1. 可变参模板(variadic template),可变参模板或者类,对代码的复用性又有了一个显著得提升。模板已经解决了类型上的障碍,臂如你可在模板template,对T取得多种类型进行构造或者实例化。但是类型这一层障碍突破了之后吧,仍然没有解决数量的问题,臂如在类中或者函数中参数数量是确定,臂如两个参数,三个参数,仍旧需要实现多个版本的样例。可变参解决了这个问题,以一种诡异的技巧实现参数任意化。
    下面贴代码举栗子:
1. template<typename ...Element> class tuple;
2. tuple<int, string> a;  // use it like this

Element左边出现…表示Element是一个类型参数包,c++11引入的特性。第二行的,int,string集合就是被它代表,是这个参数的合集。上述类型模板可以这样做,非类型模板参数也可以这样做:

1.template<typename T, unsigned PrimaryDimesion,unsigned...Dimesions>
class array { /**/ };
2.array<double, 3, 3> rotation_matrix; //3x3 ratiation matrix

变参模板是怎么抽取参数呢?通过模板特化,递归/继承;

template<typename... Elements> class tuple;
template<typename Head, typename... Tail>
class tuple<Head, Tail...> : private tuple<Tail...> {
    Head head;
public:
    /* implementation */
};
template<>
class tuple<> {
    /* zero-tuple implementation */
};

tujie
看懂这幅图,就能大致理解。

2.Lambda表达式,Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用,比如可以将它们赋给变量和作为参数传递,还可以像函数一样对其求值。
举个例子:
sort排序时候,需要自己实现比较函数。

#include <stdlib.h>
#include <stdio.h>
static int intcompare(const void *p1, const void *p2)
{
  int i = *((int *)p1);
  int j = *((int *)p2);
  return (i < j) ;
}
int main()
{
  int a[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
  qsort((void *)a, 10, sizeof(int), intcompare);
  for (int i = 0; i < 10; i++) { printf("%d ", a[i]); }
  printf("\n");
  return 0;
}

不足之处:
1.比较函数需要单独声明。这增加了将错误的比较函数传递给 qsort() 操作的风险。
2.比较函数接受 void * 参数,因此缺失了某种程度的类型检查。
3.比较函数看不到任何局部作用的变量。因此,如果存在其他影响排序的因素,必须在更大范围内声明。

#include <algorithm>
int main()
{
  int a[10] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
  std::sort( a, &a[10], [](int x, int y){ return x < y; } );
  for(int i=0; i<10; i++) { printf("%i ", a[i]); }
  printf("\n");
  return 0;
}
  1. 智能指针
    auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三个是c++11支持,并且第一个已经被c++11弃用。
    c++的内存管理是让很多人头疼的事,当我们写一个new语句时,一般就会立即把delete语句直接也写了,但是我们不能避免程序还未执行到delete时就跳转了或者在函数中没有执行到最后的delete语句就返回了,如果我们不在每一个可能跳转或者返回的语句前释放资源,就会造成内存泄露。使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个类,当超出了类的作用域是,类会自动调用析构函数,析构函数会自动释放资源。
    auto_ptr智能指针可以像类的原始指针一样访问类的public成员,成员函数get()返回一个原始的指针,成员函数reset()重新绑定指向的对象,而原来的对象则会被释放。注意我们访问auto_ptr的成员函数时用的是“.”,访问指向对象的成员时用的是“->”。我们也可用声明一个空智能指针auto_ptrptest();
    当我们对智能指针进行赋值时,如ptest2 = ptest,ptest2会接管ptest原来的内存管理权,ptest会变为空指针,如果ptest2原来不为空,则它会释放原来的资源,基于这个原因,应该避免把auto_ptr放到容器中,因为算法对容器操作时,很难避免STL内部对容器实现了赋值传递操作,这样会使容器中很多元素被置为NULL。判断一个智能指针是否为空不能使用if(ptest == NULL),应该使用if(ptest.get() == NULL),如下代:
int main()
{
    auto_ptr<Test> ptest(new Test("123"));
    auto_ptr<Test> ptest2(new Test("456"));
    ptest2 = ptest;
    ptest2->print();
    if(ptest.get() == NULL)cout<<"ptest = NULL\n";
    return 0;
}

unique_ptr 是一个独享所有权的智能指针,它提供了严格意义上的所有权,包括:

  1. 拥有它指向的对象
  2. 无法进行复制构造,无法进行复制赋值操作。即无法使两个unique_ptr指向同一个对象。但是可以进行移动构造和移动赋值操作
  3. 保存指向某个对象的指针,当它本身被删除释放的时候,会使用给定的删除器释放它指向的对象

unique_ptr 可以实现如下功能:

  1. 为动态申请的内存提供异常安全

  2. 讲动态申请的内存所有权传递给某函数

  3. 从某个函数返回动态申请内存的所有权

  4. 在容器中保存指针
    使用函数的返回值赋值时,可以直接使用=, 这里使用c++11 的移动语义特性。另外注意的是当把它当做参数传递给函数时(使用值传递,应用传递时不用这样),传实参时也要使用std::move,比如foo(std::move(ptest))。它还增加了一个成员函数swap用于交换两个智能指针的值。

share_ptr资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。出了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,计数减一。当计数等于0时,资源会被释放。

int main()
{
    shared_ptr<Test> ptest(new Test("123"));
    shared_ptr<Test> ptest2(new Test("456"));
    cout<<ptest2->getStr()<<endl;
    cout<<ptest2.use_count()<<endl;
    ptest = ptest2;//"456"引用次数加1,“123”销毁
    //ptest所指的对象,指向了ptest2的所指的对象。此时两个智能指针的引用计数都是2
    ptest->print();
    cout<<ptest2.use_count()<<endl;//2
    cout<<ptest.use_count()<<endl;//2
    ptest.reset();
    ptest2.reset();//此时“456”销毁
    cout<<"done !\n";
    return 0;
}

weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

  1. 类型重命名(alias template)
    using的用法在这里不展开,主要谈一下,using在指定别名中的用法的原因:
    <1.> 提高代码阅读可读性
typedef std::unique_ptr<std::unordered_map<std::string, std::string>> UPtrMapSS;
using UPtrMapSS = std::unique_ptr<std::unordered_map<std::string, std::string>>;
typedef void (*FP) (int, const std::string&); //函数指针
using FP = void (*) (int, const std::string&);//函数指针
typedef std::string (Foo::* fooMemFnPtr) (const std::string&);
 
using fooMemFnPtr = std::string (Foo::*) (const std::string&);

<2.> 模板类型,alias template模板别名

template <typename T>
typedef MyVector<T, MyAlloc<T>> Vec;
// usage
Vec<int> vec;

这种给模板重命名类型会报错。

template <typename T>
using Vec = MyVector<T, MyAlloc<T>>;
 
// usage
Vec<int> vec;

这样才正确。
5. 右值引用与引用
C++( 包括 C) 中所有的表达式和变量要么是左值,要么是右值。通俗的左值的定义就是非临时对象,那些可以在多条语句中使用的对象。所有的变量都满足这个定义,在多条代码中都可以使用,都是左值。右值是指临时的对象,它们只在当前的语句中有效。
右值引用 (Rvalue Referene) 是 C++ 新标准 (C++11, 11 代表 2011 年 ) 中引入的新特性 , 它实现了转移语义 (Move Sementics) 和精确传递 (Perfect Forwarding)。它的主要目的有两个方面:
<1.>消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。
<2.>能够更简洁明确地定义泛型函数。
转移语义:
右值引用是用来支持转移语义的。转移语义可以将资源 ( 堆,系统对象等 ) 从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高 C++ 应用程序的性能。临时对象的维护 ( 创建和销毁 ) 对性能有严重影响。
精确传输:

template <typename T> void forward_value(const T& val) { 
 process_value(val); 
} 
template <typename T> void forward_value(T& val) { 
 process_value(val); 
}
template <typename T> void forward_value(T&& val) { 
 process_value(val); 
}

只需要定义一次,接受一个右值引用的参数,就能够将所有的参数类型原封不动的传递给目标函数。四种不用类型参数的调用都能满足,参数的左右值属性和 const/non-cosnt 属性完全传递给目标函数 process_value.

  1. 线程类
    多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。
    基于进程的多任务处理是程序的并发执行。
    基于线程的多任务处理是同一程序的片段的并发执行。
#include <pthread.h>
pthread_create (thread, attr, start_routine, arg) 

参数 描述
thread 指向线程标识符指针。
attr 一个不透明的属性对象,可以被用来设置线程属性。您可以指定线程属性对象,也可以使用默认值 NULL。
start_routine 线程运行函数起始地址,一旦线程被创建就会执行。
arg 运行函数的参数。它必须通过把引用作为指针强制转换为 void 类型进行传递。如果没有传递参数,则使用 NULL。

bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    std::thread t1(&HelloWorld::myThread,this);//创建一个分支线程,回调到myThread函数里
    t1.join();
    //t1.detach();
    CCLOG("in major thread");//在主线程
    return true;
}
 
void HelloWorld::myThread()
{
    CCLOG("in my thread");
}

t.join()等待子线程myThread执行完之后,主线程才可以继续执行下去,此时主线程会释放掉执行完后的子线程资源。从上面的图片也可以看出,是先输出"in my thread",再输出"in major thread"。
当然了,如果不想等待子线程,可以在主线程里面执行t1.detach()将子线程从主线程里分离,子线程执行完成后会自己释放掉资源。分离后的线程,主线程将对它没有控制权了。如下:

std::thread t1(&HelloWorld::myThread,this);//创建一个分支线程,回调到myThread函数里
t1.detach();

3. 代码效率的总结

在阅读代码过程中体会到了效率上极致的追求以及一些之前未接触过的编程手法

  1. constexpr
  2. inline
  3. default和delete
  4. 移植性(uint64_t)
  5. 命名空间嵌套
  6. 宏中抛异常,宏的效率高于const
  7. unordered_map和share_mutex
  8. 右值,移动语义

三,其他

暂无

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值