C/C++编程心得
strtok
strtok函数多用于分割字符串,但它会改变被分割字符串的值。因此,如果该字符串以后还有用的话,需要首先复制该字符串,然后对复制的字符串执行strtok函数。
malloc与calloc
两者都是动态分配内存。
主要的不同:malloc不初始化分配的内存,已分配的内存中可以是任意的值。calloc初始化已分配的内存为0。
次要的不同:calloc返回的是一个数组,而malloc返回的是一个对象。
惰性求值
http://www.cnblogs.com/gtarcoder/p/4811614.html
c++11实现l延迟调用(惰性求值)
http://www.fuzihao.org/blog/2016/02/10/C-%E5%AE%9E%E7%8E%B0%E6%83%B0%E6%80%A7%E6%B1%82%E5%80%BC/
C++实现惰性求值
gcc链接器
gcc和ld的差异
理论上gcc做链接和ld做链接,应该是一样的效果,然而实际情况要复杂一些。有的厂商的工具链会给gcc添加一些环境变量之类的私货,所以两者的行为就变的很有差异了。遇到这种问题,互换是一种好的解决问题的思路。
链接顺序
有的链接器对链接顺序有要求,一般按照c代码、自定义库、标准库的顺序来链接,也就是越基础底层的库,越在后面。(这个顺序正好和声明的顺序相反)
gcc -c ./sparse_matrix.c -o sparse_matrix.o -luserlib -lm
但是如果有一系列很底层的库,他们太底层了,以至于会出现相互依赖的情况(circular dependence),那gcc提供了一个option很好的解决了这个情况:
-Wl,--start-group -lmy_lib -lyour_lib -lhis_lib -Wl,--end-group
再比如下面的例子:
https://github.com/antkillerfarm/antkillerfarm_crazy/tree/master/helloworld/linux_so
gcc -o main_link main_link.c -L. -lhello
这条命令中的main_link.c如果放到-lhello
之后就会出问题。也考虑使用--start-group
和--end-group
之类的链接选项解决链接顺序问题。
参考:
https://stackoverflow.com/questions/27475977/c-undefined-reference-to-sqrt-even-with-lm
C - undefined reference to “sqrt” even with ‘-lm’
左值和右值
左值是可以放在赋值号左边可以被赋值的值;左值必须要在内存中有实体;
右值当在赋值号右边取出值赋给其他变量的值;右值可以在内存也可以在CPU寄存器。
一个对象被用作右值时,使用的是它的内容(值),被当作左值时,使用的是它的地址。
左值语法:type &引用名 = 左值表达式;
右值语法:type &&引用名 = 右值表达式;
C++11通过引入右值引用来优化性能,具体来说是通过move语义来将临时生成的左值中的资源无代价的转移到另外一个对象中去。
但右值引入也带来的新问题。
原来我们只有:
template<typename T> void f1(T& t);
现在还有:
template<typename T> void f2(T&& t);
不加限制的话,还会有T&& &
(将T&&
作为参数传给f1),所以就有了引用折叠(Reference collapsing)的概念,即:
& & => &
&& & => &
& && => &
&& && => &&
如果把上述规则看作logical-OR的话,那么&
就是1,而&&
就是0。
注意,因为临时变量是右值,所以T&&
的类型在f2函数体内实际上就是T
。
std::move:无条件的转为右值引用。
std::forward:有条件的转为右值引用。
std::remove_reference:将T&
或T&&
变成T
。
参考:
https://www.cnblogs.com/ldlchina/p/6608154.html
C++11右值引用和std::move语句实例解析
https://www.jianshu.com/p/b90d1091a4ff
C++11 std::move和std::forward
http://shaoyuan1943.github.io/2016/03/26/explain-move-forward/
详解C++11中移动语义(std::move)和完美转发(std::forward)
https://eli.thegreenplace.net/2014/perfect-forwarding-and-universal-references-in-c/
Perfect forwarding and universal references in C++
RAII
资源获取即初始化(Resource Acquisition Is Initialization),或称RAII,是一种C++编程技术,它将必须在使用前请求的资源(分配的堆内存、执行线程、打开的套接字、打开的文件、锁定的互斥体、磁盘空间、数据库连接等——任何存在受限供给中的事物)的生命周期绑定与一个对象的生存期相绑定。
C++标准库遵循RAII管理其自身的资源:类在构造函数中获取其资源(错误时抛出异常),并在其析构函数中释放之(决不抛出),而不要求显式清理。
RAII不适用于并非在使用前请求的资源:CPU 时间、核心,以及缓存容量、熵池容量、网络带宽、电力消费、栈内存等。
RAII也是下面介绍的各类智能指针的理论基础。
参考:
https://mp.weixin.qq.com/s/m44zVWMvactsLPjKsFnlSg
RAII惯用法:C++资源管理的利器
各类ptr
auto_ptr:它允许程序员创建一个指向某种资源的指针对象,当该对象离开它的作用域时,它所指向的资源也会被自动释放。
在原本的C++中,new和delete必须配对使用,然而给每个异常处理分支添加delete是一件很麻烦的事。auto_ptr就是用来干这事的,它无需显式调用delete。
对auto_ptr的赋值和拷贝会导致原来的auto_ptr变为NULL,从而无效化。
unique_ptr:auto_ptr是全局唯一的,且不采用引用计数管理,因此,赋值和拷贝都是不必要的,且会造成混淆。(尤其拷贝会导致原来的auto_ptr变为NULL,这个还能叫做拷贝吗?)因此,auto_ptr在后续标准中,不再推荐使用,而是用unique_ptr替代。unique_ptr默认不支持赋值和拷贝,确需使用,要与std::move配合方可。否则会编译出错。
unique_ptr早先在boost库中的时候,也叫做scoped_ptr。
shared_ptr:为多个拥有者管理内存中对象的生命周期而设计的。在你初始化一个 shared_ptr 后,你可以复制它,把函数参数的值递给它,并把它分配给其它 shared_ptr 实例。所有实例指向同一个对象,并共享访问一个“控制块”,即每当一个新的shared_ptr 被添加时,递增和递减引用计数,超出范围,则复位。当引用计数到达零时,控制块删除内存资源和自身。
weak_ptr:如果对象A中有对象B的shared_ptr,而对象B中又有对象A的shared_ptr,那么就会出现循环引用的情况。这时可以使用weak_ptr。两者的区别在于新建shared_ptr会增加引用计数,而weak_ptr不会。
上述指针还可以自己定义deleter:
void close_file(std::FILE* fp) { std::fclose(fp); }
std::unique_ptr<std::FILE, decltype(&close_file)> fp(std::fopen("demo.txt", "r"),&close_file);
上面的close_file
就是自定义的deleter,用来进行特殊的析构。
上述ptr不仅可以保存对象的指针,还可以保存数组指针:
shared_ptr<double[]> p2( new double[n] );
此外ptr的声明方式也有两种(以unique_ptr为例):
-
方法1:
std::unique_ptr<int>(new int(1));
-
方法2:
std::make_unique<int>(1);
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