Antkillerfarm Hacking V7.5

language » C/C++编程心得(三)

2019-07-04 :: 7684 Words

各类ptr(续)

多线程的weak_ptr需要使用enable_shared_from_this机制来把this从类内部传递出来,来持有shared_ptr。使用时,用weak_ptr的lock函数返回shared_ptr,如果shared_ptr为null,则表明该对象已经被析构了。


参考:

https://mp.weixin.qq.com/s/32aeGOPaySjTmyKcjFKgYA

窥见C++11智能指针

https://www.cnblogs.com/wangkeqin/p/9351191.html

C++内存管理之shared_ptr

https://blog.csdn.net/River_Lethe/article/details/78734879

shared_ptr的使用和陷阱

https://blog.csdn.net/qq_38410730/article/details/105903979

weak_ptr弱引用智能指针详解

decltype

和auto的用法类似,auto不仅要推导类型,还要定义变量,而decltype则只进行类型推导。

int add(int x,int y){
    return x+y;
}
int main(){
    double i=0;
    decltype(i) a; // double
    decltype(add()) b; //int 注意括号。不带括号就是函数指针了。
}

参考:

https://www.cnblogs.com/npbool/p/3433360.html

C++11初探:类型推导,auto和decltype

类型转换

static_cast:基本等同于C语言的强制类型转换,只有很小的差异。

dynamic_cast:主要用于类层次间的上行转换和下行转换。

const_cast:用于修改类型的const或volatile属性。

reinterpret_cast:它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针。

上面4个算是C++的基本类型变换。除此之外还有一些变种。

  • 和shared_ptr配套的类型变换:

static_pointer_cast:Static cast of shared_ptr

dynamic_pointer_cast:Dynamic cast of shared_ptr

const_pointer_cast:Const cast of shared_ptr

  • dynamic_cast的改进版(主要是增加了一些断言或者异常处理):
template <class Derived, class Base>
inline Derived polymorphic_cast(Base* x);
// Throws: std::bad_cast if ( dynamic_cast<Derived>(x) == 0 )
// Returns: dynamic_cast<Derived>(x)

template <class Derived, class Base>
inline Derived polymorphic_downcast(Base* x);
// Effects: assert( dynamic_cast<Derived>(x) == x );
// Returns: static_cast<Derived>(x)

template <class Derived, class Base>
inline auto polymorphic_pointer_cast(Base x);
// Throws: std::bad_cast if ( dynamic_pointer_cast<Derived>(x) == 0 )
// Returns: dynamic_pointer_cast<Derived>(x)

template <class Derived, class Base>
inline auto polymorphic_pointer_downcast(Base x);
// Effects: assert( dynamic_pointer_cast<Derived>(x) == x );
// Returns: static_pointer_cast<Derived>(x)

参考:

https://www.cnblogs.com/chenyangchun/p/6795923.html

C++强制类型转换:static_cast、dynamic_cast、const_cast、reinterpret_cast

std::bind & std::placeholders

void f(int n1, int n2, int n3, const int& n4, int n5)
{
    std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
}

auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
f2(10, 11, 12); // 进行到 f(12, g(12), 12, 4, 5); 的调用

不定参数

int sum(int count, ...)
{
    va_list vl;
    int sum = 0;
    va_start(vl, count);
    for (int i = 0; i < count; ++i)
    {
        sum += va_arg(vl, int);
    }
    va_end(vl);
    return sum;
}

上面是C风格的变参函数,主要依赖了va_xx系列函数。

template<typename T>
void Append(Optimizer::Optimizations& optimizations, T&& optimization)
{
    optimizations.emplace_back(new T(optimization));
};

template<typename Front, typename... Others>
void Append(Optimizer::Optimizations& optimizations, Front&& front, Others&&... others)
{
    Append<Front>(optimizations, std::forward<Front>(front));
    Append<Others...>(optimizations, std::forward<Others>(others)...);
};

template<typename... Args>
Optimizer::Optimizations MakeOptimizations(Args&&... args)
{
    Optimizer::Optimizations optimizations;

    Append(optimizations, std::forward<Args>(args)...);

    return optimizations;
}

上面是C++风格的变参函数。它主要采用了Parameter pack技术,简单的说就是递归的模板展开。

以上面的函数为例:

1.首先使用第2个模板函数展开函数。

2.在第2个模板函数中调用第1个模板函数,而第1个模板函数不是变参函数,相当于是递归的结尾。

std::tuple、std::bind等的实现都借助了Parameter pack。

在C++17中,还提出一个叫做fold expression的技术。

template<typename ...Args>
int sum(Args&&... args) {
//    return (args + ... + 1 * 2); // Error: operator with precedence below cast
    return (args + ... + (1 * 2)); // OK
}

上述函数是一个对+运算符的fold expression。

再来一个dispatch的例子:

CheckDispatch<bool, float, double, uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t>();

template <typename Type, typename Next, typename... Types>
void CheckDispatch() {
  if (type == GetType<Type>())
    Check<Type>();
  else
    CheckDispatch<Next, Types...>();
}

template <>
void Check<float>() {
    //do sth
}

参考:

https://www.jianshu.com/p/d22904f30930

C++11新特性–不定参数模板与std::tuple、std::bind实现原理

特化(traits)

template<class T1, class T2>      // 普通版本,有两个模板参数
class B { ..... };

template<class T2>         // 偏特化版本,指定其中一个参数,即指定了部分类型
class B<int , T2> { ..... };// 当实例化时的第一个参数为int 则会优先调用这个版本

偏特化的条件:

1.必须有一个主模板。

2.模板类型被部分明确化。

相应的,如果模板参数全被指定,则为全特化

对主版本模板类、全特化类、偏特化类的调用优先级从高到低进行排序是:全特化类>偏特化类>主版本模板类。

traits一方面,在面对不同的输入类时,能找到合适的返回型别;另一方面,当型别对应有不同的实现函数的时候,能起到一个提取型别然后分流的作用。

标准库已经内置了一些常见的traits操作,例如std::pointer_traits,该模板可返回迭代器指向对象的型别。

参考:

https://www.cnblogs.com/yyehl/p/7253254.html

C++ 模板偏特化-来自STL的思考

https://www.cnblogs.com/mangoyuan/p/6446046.html

C++ traits技术浅谈

typename

vector<T>::size_type可能有三种解释:

  • 静态数据成员

  • 静态成员函数

  • 嵌套类型

前两者比较好区分,函数名后一般有括号。但是嵌套类型就不好区分了,因此需要用typename关键字指定之。

参考:

https://blog.csdn.net/zhangxiao93/article/details/50569924

C++ typedef typename作用

typeid

typeid可用于打印类型名称。这里的类型可以是固定类型,也可以是模板类型。

cout << typeid(int).name() << typeid(T).name() << endl;

以下是一个复杂的例子,用于处理shared_ptr+派生类的情况:

std::vector<std::shared_ptr<A>> a;
class B : public A;
class C : public A;
a.push_back(std::make_shared<B>());
auto a0 = a[0];
cout << typeid(*a0).name() << endl;

std::reference_wrapper<const std::type_info> AInfo = typeid(A);
typeid(*p) == AInfo.get()

判断模板参数的实际类型

if (std::is_same<float, T>::value) {
    //do sth
}

Range-based for loop

for (auto i : v) // access by value, the type of i is int
    std::cout << i << ' ';

C++ thread

长期以来,C/C++的线程编程主要依赖pthread库,但是这个情况在C++ 11之后有所改观,因为C++ 11的标准库已经支持thread了。

thread基本用法:

https://www.runoob.com/w3cnote/cpp-std-thread.html

std::thread

配合thread还提出了thread_local关键字:

https://www.cnblogs.com/pop-lar/p/5123014.html

thread_local变量

mutex同步:

https://www.cnblogs.com/xudong-bupt/p/9194394.html

C++并发编程,std::unique_lock与std::lock_guard区别示例

条件等待:

http://hengyunabc.github.io/cpp11-mutex-lock-condition/

C++11中的mutex, lock,condition variable实现分析

https://www.zhihu.com/question/24116967

pthread_cond_wait为什么需要传递mutex参数?

https://segmentfault.com/a/1190000006679917

条件变量(Condition Variable)

condition variable的wait操作会释放锁,并挂起当前线程。

explicit

class Point {
public:
    int x, y;
    Point(int x = 0, int y = 0)
        : x(x), y(y) {}
};

void displayPoint(const Point& p) 
{
    cout << "(" << p.x << "," 
         << p.y << ")" << endl;
}

int main()
{
    displayPoint(1);
    Point p = 1;
}

上面代码片段中,displayPoint(1);粗看起来是不能执行的,因为1不是一个Point。然而C++编译器要比C聪明一点,它发现,1是Point构造函数的合法参数,于是自动调用之。即:displayPoint(1);等价于displayPoint(Point(1));

然而有的时候,为了代码的健壮,我们不想让别人用这样不正式的方式创建对象,这时可以通过将构造函数声明为explicit,来防止隐式类型转换。

explicit关键字只能用于类内部的构造函数声明上,而不能用在类外部的函数定义上。

参考:

https://zhuanlan.zhihu.com/p/52152355

C++ explicit 关键字

模板实参推导

template<typename To, typename From> To convert(From f);
void g(double d)
{
    int i = convert<int>(d);    // 调用 convert<int, double>(double)
    char c = convert<char>(d);  // 调用 convert<char, double>(double)
    int(*ptr)(float) = convert; // 实例化 convert<int, float>(float)
}

除了特化模板之外,从函数参数推断模板参数也是可以的,如上例中的模板参数From就可以从使用该模板参数的函数参数f的类型,倒推出来。

Defaulted & Deleted

特殊成员函数主要包括:

1.默认构造函数(不带参数的构造函数)

2.析构函数

3.赋值函数(operator=)(按字节拷贝)

4.拷贝构造函数(用一个对象来初始化另一个对象,只有一个参数,参数类型是本类的引用。X(X&))(按字节拷贝)

5.移动构造函数(X(X&&)

6.移动赋值函数

其中,2、3、4被称为big three,2、3、4、5、6被称为big five。5、6是C++11中引入右值和move的结果。

https://www.jianshu.com/p/0b0f84b49736

C++缺省函数(big three,big five)


class X{
public:
  X(int i){
   a = i;
  }
  X(const X&) = delete;
  X()=default;
private:
 int a;
};

X x;
Fork me on GitHub