C/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);

从实现来看,这些智能指针除了需要new对象之外,还需要new一个用于维护对象引用计数的控制块,因此第2种方法一次性分配的效率会比较高。

一般来说,make_XXX在标准库中,通常都带有new对象和相关控制结构的操作,除了上述的make_unique之外,类似的还有make_pair等。


shared_ptr既然号称共享,那自然有一定的共享规则在:

shared_ptr<int> sp1(new int(10));
shared_ptr<int> sp2(sp1), sp3;
sp3 = sp1;
//一个典型的错误用法
shared_ptr<int> sp4(sp1.get()); 
cout << sp1.use_count() << " " << sp2.use_count() << " " 
<< sp3.use_count() << " " << sp4.use_count() << endl;
//输出 3 3 3 1

sp1,sp2,sp3是相互关联的共享指针,共同控制所指内存的生存期,sp4虽然指向同样的内存,却是与sp1,sp2,sp3独立的,sp4按自己的引用计数来关联内存的释放。


auto p = std::make_unique<XXX>();
void f(XXX*);
f(p); // error
f(p.get()); // OK

虽然,类似p->YYY()的用法和普通指针一致,但是作为f函数参数的时候,就不行了。


参考:

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的使用和陷阱

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。

参考:

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;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值