1

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

 2 years ago
source link: http://antkillerfarm.github.io/language/2019/07/04/c_study_3.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

类型转换(续)

  • 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;

判断模板参数的实际类型

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

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)

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 关键字

std::tuple

auto tuple = std::make_tuple(1, 'A', "test");
std::cout << std::get<0>(tuple) << std::endl;
std::cout << std::get<1>(tuple) << std::endl;
std::cout << std::get<2>(tuple) << std::endl;

std::tie

可用tuple或pair返回多个返回值,然后用tie进行解包。这里的语法非常类似python。

std::pair<int, std::string> fun_tie(int a, std::string str)
{
  return std::make_pair(a, str);
}

int a;
std::string str;
std::tie(a , str) = fun_tie(12, std::string("Pony Ma"));
std::cout << a << "," << str << std::endl;

模板实参推导

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.拷贝构造函数(用一个对象来初始化另一个对象,只有一个参数,参数类型是本类的引用。)(按字节拷贝)

5.移动构造函数

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;

由于已经有了X(int i),编译器就不会生成默认的X()了,因此X x;会出错。解决办法是X()=default;

如果想禁用默认函数的话,可以使用=delete。上例中,就是禁用默认的拷贝构造函数的示例。

default仅用于类的特殊成员函数,且该特殊成员函数没有默认参数。

class X {
public:
    int f() = default: //错误,f()非特殊成员函数
    X(int) = default; //错误,非默认构造函数
    X(int i = 1) = default; // 错误,含有默认参数
}

https://www.ibm.com/developerworks/cn/aix/library/1212_lufang_c11new/index.html

C++11标准新特性:Defaulted和Deleted函数

打印vector的N种方法

auto vec = make_index_vector(std::make_index_sequence<10>());
for(auto i : vec) {std::cout << i << ' ';}
std::cout << std::endl;
std::iota(vec.begin(), vec.end(), 999);
std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
std::for_each(std::begin(vec),std::end(vec),[](int n){std::cout<<n<<" ";});
std::cout << std::endl;

const

class A
{
   void function() const; //常成员函数, 它不改变对象的成员变量,
                        //也不能调用类中任何非const的成员函数。
} 

K&R风格

在C语言的函数定义上,我们通常用的函数定义方式为ANSI C的函数定义方式。但是在C语言之父创立C语言之时,函数的定义形式并非现在我们所见到的形式。

这种风格被称为K&R风格,多见于一些历史悠久的项目或者老的书籍中。出于兼容性考虑,现代的C编译器仍然支持K&R风格。

记得大学学C语言,用的是谭浩强的书。当时,授课老师看不上谭的书,于是另外推荐了一本,那本书更古老,用的就是所谓的K&R风格。。。但在实际工作中,从来没见过K&R风格的代码。。。

http://blog.chinaunix.net/uid-7426920-id-2627743.html

ANSI和K&R两种函数定义风格


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK