最近学习解读pytorch源码(详见链接),代码中C++11的部分有很多未尝涉及,遂进行总结。
shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每构造一次,内部的引用计数加1;每析构一次,内部的引用计数减1;减为0时,删除所指向的堆内存。
shared_ptr内部的引用计数是安全的,但是对象的读取需要加锁。
初始化:可以通过构造函数、std::make_shared辅助函数和reset方法来初始化shared_ptr
#include "stdafx.h"
#include <iostream>
#include <future>
#include <thread>
using namespace std;
class Person
{
public:
Person(int v) {
value = v;
std::cout << "Cons" <<value<< std::endl;
}
~Person() {
std::cout << "Des" <<value<< std::endl;
}
int value;
};
int main()
{
std::shared_ptr<Person> p1(new Person(1));// Person(1)的引用计数为1
std::shared_ptr<Person> p2 = std::make_shared<Person>(2);
p1.reset(new Person(3));// 首先生成新对象,然后引用计数减1,引用计数为0,故析构Person(1)
// 最后将新对象的指针交给智能指针
std::shared_ptr<Person> p3 = p1;//现在p1和p3同时指向Person(3),Person(3)的引用计数为2
p1.reset();//Person(3)的引用计数为1
p3.reset();//Person(3)的引用计数为0,析构Person(3)
return 0;
}
避免循环引用。智能指针最大的一个陷阱是循环引用,循环引用会导致内存泄漏。解决方法是引入weak_ptr。
一些资源(如文件描述符、锁等),为了确保唯一性,同时指向它的指针始终只有一个
#include <memory>
#include <iostream>
#include <utility>
class Foo {
public:
Foo() = default;
Foo(int a) :_a(a) {}
~Foo() {}
int get_a() {
return _a;
}
void set_a(int a) {
_a = a;
}
private:
int _a;
};
std::unique_ptr<Foo> change_a(std::unique_ptr<Foo> f)
{
f->set_a(10);
return f;
}
int main()
{
std::unique_ptr<Foo> pf = std::make_unique<Foo>(10);
//如下操作无法实现:
// std::unique_ptr<Foo> pf1 = pf; // compile error
// auto p = change_a(pf); //compile error
//--------------------move---------------------------
//std::move(lvalue)将左值转换为右值,使接下来的转移成为可能。
//pf为左值,转换为右值才可以对unique_ptr进行转移赋值
//如下转移赋值后,pf释放对象,p指向对象
//--------------------forward------------------------
//std::forward就可以保存参数的左值或右值特性,还可以保持实参的类型、是否const
auto p = change_a(std::move(pf));
std::cout << "get_a = " << p->get_a() << std::endl;
if (!pf)
{
std::cout << "pf is nullptr" << std::endl;
}
//owner transfer from function
std::unique_ptr<Foo> pf2 = std::make_unique<Foo>(11);
std::unique_ptr<Foo> p2 = change_a(std::move(pf2));
std::cout << "get_a = " << p2->get_a() << std::endl;
if (!pf2)
{
std::cout << "pf2 is nullptr" << std::endl;
}
//使用reset
pf2.reset(new Foo(12));
std::cout << "pf2 is not null: " << pf2->get_a() << std::endl;
//release获取原始指针
Foo* ff = pf2.release();
if (!pf2)
{
std::cout << "pf2 is nullptr" << std::endl;
}
std::cout << "ff is not null: " << ff->get_a() << std::endl;
return 0;
}
get_a = 10
pf is nullptr
get_a = 10
pf2 is nullptr
pf2 is not null: 12
pf2 is nullptr
ff is not null: 12
解决shared_ptr的循环引用问题,经典错误示例如下:
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
class ClassA
{
public:
ClassA() { cout << "ClassA Constructor..." << endl; }
~ClassA() { cout << "ClassA Destructor..." << endl; }
shared_ptr<ClassB> pb; // 在A中引用B
};
class ClassB
{
public:
ClassB() { cout << "ClassB Constructor..." << endl; }
~ClassB() { cout << "ClassB Destructor..." << endl; }
shared_ptr<ClassA> pa; // 在B中引用A
};
int main() {
shared_ptr<ClassA> spa = make_shared<ClassA>();
shared_ptr<ClassB> spb = make_shared<ClassB>();
spa->pb = spb;
spb->pa = spa;
}
weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,即将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。
改进后的代码:
class ClassA
{
public:
ClassA() { cout << "ClassA Constructor..." << endl; }
~ClassA() { cout << "ClassA Destructor..." << endl; }
weak_ptr<ClassB> pb; // 在A中引用B
};
class ClassB
{
public:
ClassB() { cout << "ClassB Constructor..." << endl; }
~ClassB() { cout << "ClassB Destructor..." << endl; }
weak_ptr<ClassA> pa; // 在B中引用A
};
int main() {
shared_ptr<ClassA> spa = make_shared<ClassA>();
shared_ptr<ClassB> spb = make_shared<ClassB>();
spa->pb = spb;
spb->pa = spa;
}
运行结果:
ClassA Constructor...
ClassB Constructor...
ClassA Destructor...
ClassB Destructor...
Program ended with exit code: 0
lock(): 判断weak_ptr指向的对象是否被释放,lock()函数返回一个指向共享对象的shared_ptr,否则返回一个空的shared_ptr
class A
{
public:
A() : a(3) { cout << "A Constructor..." << endl; }
~A() { cout << "A Destructor..." << endl; }
int a;
};
int main() {
shared_ptr<A> sp(new A());
weak_ptr<A> wp(sp);
//sp.reset();
if (shared_ptr<A> pa = wp.lock())
{
cout << pa->a << endl;
}
else
{
cout << "wp指向对象为空" << endl;
}
}
详见
explicit作用: 在C++中,explicit关键字用来修饰类的构造函数,被修饰的构造函数的类,不能发生相应的隐式类型转换,只能以显示的方式进行类型转换。
详见链接:
静态成员变量(面向对象)
静态数据区分配内存,到程序结束释放
同类的多个对象数据共享
静态成员函数(面向对象)
1.静态成员函数无法访问非静态成员变量和非静态成员函数
2.没有this指针的额外开销,静态成员函数与类的全局函数相比速度稍快
静态全局变量(面向过程)
1.在全局数据区分配内存
2.全局变量就可以实现变量在文件间的共享 vs 静态全局变量不能被其它文件所用
静态局部变量(面向过程)
1.在全局数据区分配内存
2.程序执行到静态局部变量声明处被首次初始化,即以后的函数调用不再进行初始化;
3.没有显式初始化,自动初始化为0;
4.静态局部变量始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域(对比静态全局变量),当定义它的函数或语句块结束时,其作用域随之结束;
静态函数(面向过程)
它只能在声明它的文件当中可见,不能被其它文件使用
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- dcrkj.com 版权所有 赣ICP备2024042791号-2
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务