C/C++编程心得(三)
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.
类型转换(续)
- 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两种函数定义风格
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK