c++高阶语法(一)
时间:2023-03-01 05:00:00
前面已经有一切了c 语法是比较基础的c 以后博主会分析一些语法c 对于每个人来说,提高更高级的语法c 的理解
1)智能指针:
智能指针就是帮我们C 如果程序员管理动态分配的内存,它将帮助我们自动释放new从而产生内存避免内存泄漏!在我们知道内存分配后,我们必须释放它。当内存未释放时,内存会泄露,造成不良影响。
因此,智能指针是为了解决指针自动释放的问题。让我们来看看智能指针的相关内容。
一,auto_ptr
auto_ptr 是c 98定义的智能指针模板定义了管理指针的对象new 给这个对象一个获得(直接或间接)的地址。当对象过期时,将使用其分析函数delete 释放内存!
用法:
头文件: #include < memory >
用 法: auto_ptr<类型> 变量名(new 类型)
例子:
class Test {
public:
Test() { cout << "Test的构造函数..." << endl; }
~Test() { cout << "Test的析构函数..." << endl; }
int getDebug() { return this->debug; }
private:
int debug = 20;
};
int main(void) {
//Test *test = new Test;///智能指针没有定义,所以内存泄露,分析函数的内容不会打印
auto_ptr
cout << "test->debug:" << test->getDebug() << endl;
cout << "(*test).debug:" << (*test).getDebug() << endl;
return 0;
}
智能指针的三个常用函数:
1.get() 获取智能指针托管的指针地址
// 定义智能指针
auto_ptr
Test *tmp = test.get(); // 获取指针返回
cout << "tmp->debug:" << tmp->getDebug() << endl;
但除非有特殊情况,否则我们通常不会这样使用,因为我们可以直接使用智能指针。
2.release() 取消智能指针对动态内存的托管
// 定义智能指针
auto_ptr
Test *tmp2 = test.release(); // 取消智能指针对动态内存的托管
delete tmp2; // 之前分配的内存需要手动释放
也就是说,智能指针不再管理指针,而是由管理员管理!
3.reset() 重置智能指针托管的内存地址,如果地址不一致,将被分析
// 定义智能指针
auto_ptr
test.reset(); // 释放智能指针托管的指针内存,并将其放置NULL
test.reset(new Test()); // 释放智能指针托管的指针内存,替换参数指针
reset函数将指出参数(不指定为NULL),与托管指针相比,如果地址不一致,则将对原托管指针进行分析,然后用参数指针代替。然后智能指针将托管参数指针。
不过,C 11后不建议使用auto_ptr,已使用unique_ptr替代品。这里就不详细说缺点了。有兴趣可以上网了解一下。
二.unique_ptr
unique_ptr 和 auto_ptr除了一些特殊的,用法几乎相同。
unique_ptr特性
- 基于排他所有权模式:两个指针不能指向相同的资源
- 左值无法执行unique_ptr复制构造,也左值无法执行复制赋值操作,但允许临时右值赋值构造和赋值
- 当它离开作用域时,保存指向对象的指针,自动释放指向对象。
- 在容器中保存指针是安全的
(1)左值复制赋值操作不能进行,但允许临时右值赋值结构和赋值
例子:
unique_ptr
unique_ptr
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;
p1 = p2; // 禁止左值赋值
unique_ptr
unique_ptr
p1 = std::move(p2); // 使用move将左值转换为右值可以赋值,效果和auto_ptr赋值一样
cout << "p1 = p2 赋值后:" << endl;
cout << "p1:" << p1.get() << endl;
cout << "p2:" << p2.get() << endl;
(2)在 STL 容器中使用unique_ptr,不允许直接赋值
vector
unique_ptr
unique_ptr
vec.push_back(std::move(p3));
vec.push_back(std::move(p4));
cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;
vec[0] = vec[1]; /* 不允许直接赋值 */
vec[0] = std::move(vec[1]); // 需要使用move修饰,使得程序员知道后果
cout << "vec.at(0):" << *vec.at(0) << endl;
cout << "vec[1]:" << *vec[1] << endl;
(3)支持对象数组的内存管理
// 会自动调用delete [] 函数去释放内存
unique_ptr
除此之外,,unique_ptr的其余用法都与auto_ptr用法一致。
1.构造。
// unique_ptr
unique_ptr
// unique_ptr
unique_ptr
// unique_ptr
unique_ptr
// unique_ptr
unique_ptr
// unique_ptr
unique_ptr
// unique_ptr
unique_ptr
2.赋值
unique_ptr
unique_ptr
t7 = std::move(t8); // 必须使用移动语义,结果,t7的内存释放,t8的内存交给t7管理
t7->doSomething();
3.主动释放对象
unique_ptr
t9 = NULL;
t9 = nullptr;
t9.reset();//释放掉智能指针托管的指针内存,并将其置NULL
4.放弃对象的控制权
Test *t10 = t9.release();
5.重置
t9.reset(new Test);
三.shared_ptr
用法:1.引用计数的使用:调用use_count函数可以获得当前托管指针的引用计数。
shared_ptr
shared_ptr
// 获取智能指针管控的共享指针的数量 use_count():引用计数
cout << "sp1 use_count() = " << sp1.use_count() << endl;输入0
cout << "sp2 use_count() = " << sp2.use_count() << endl << endl;输出1
// 共享
sp1 = sp2;
cout << "sp1 use_count() = " << sp1.use_count() << endl;输入2
cout << "sp2 use_count() = " << sp2.use_count() << endl << endl;输出2
shared_ptr
cout << "sp1 use_count() = " << sp1.use_count() << endl;输出3
cout << "sp2 use_count() = " << sp2.use_count() << endl;输出3
cout << "sp2 use_count() = " << sp3.use_count() << endl << endl;输出3
如上代码,sp1 = sp2; 和 shared_ptr< Person > sp3(sp1);就是在使用引用计数了。
sp1 = sp2; --> sp1和sp2共同托管同一个指针,所以他们的引用计数为2;
shared_ptr< Person > sp3(sp1); --> sp1和sp2和sp3共同托管同一个指针,所以他们的引用计数为3;
2.几种构造
1). shared_ptr< T > sp1; 空的shared_ptr,可以指向类型为T的对象
shared_ptr
Person *person1 = new Person(1);
sp1.reset(person1); // 托管person1
2). shared_ptr< T > sp2(new T()); 定义shared_ptr,同时指向类型为T的对象
shared_ptr
shared_ptr
3). shared_ptr
shared_ptr
4). shared_ptr
shared_ptr
5). shared_ptr< T > sp6(NULL, D()); //空的shared_ptr,接受一个D类型的删除器,使用D释放内存
shared_ptr
6). shared_ptr< T > sp7(new T(), D()); //定义shared_ptr,指向类型为T的对象,接受一个D类型的删除器,使用D删除器来释放内存
shared_ptr
3.初始化
方式一:1.构造函数
shared_ptr
shared_ptr
2.方式二:使用make_shared 初始化对象,分配内存效率更高(推荐使用)
make_shared函数的主要功能是在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr; 用法:make_shared<类型>(构造类型对象需要的参数列表);
shared_ptr
shared_ptr
shared_ptr
4.赋值
shared_ptrr
shared_ptr
up1 = up2; // int(10) 的引用计数减1,计数归零内存释放,up2共享int(11)给up1, int(11)的引用计数为2,这种赋值方式是允许的哦
5.主动释放对象
shared_ptrr
up1 = nullptr ; // int(10) 的引用计数减1,计数归零内存释放 // 或 up1 = NULL; // 作用同上
6.重置
p.reset() ; 将p重置为空指针,所管理对象引用计数 减1
p.reset(p1); 将p重置为p1(的值),p 管控的对象计数减1,p接管对p1指针的管控
p.reset(p1,d); 将p重置为p1(的值),p 管控的对象计数减1并使用d作为删除器
p1是一个指针!
7.交换
p1 和 p2 是智能指针
std::swap(p1,p2); // 交换p1 和p2 管理的对象,原对象的引用计数不变
p1.swap(p2); // 交换p1 和p2 管理的对象,原对象的引用计数不变
注意:使用shared_ptr智能指针时,要注意避免对象交叉使用智能指针的情况!
实例:
#include
#include
#include
using namespace std;
class Girl;
class Boy {
public:
Boy() {
cout << "Boy 构造函数" << endl;
}
~Boy() {
cout << "~Boy 析构函数" << endl;
}
void setGirlFriend(shared_ptr
this->girlFriend = _girlFriend;
}
private:
shared_ptr
};
class Girl {
public:
Girl() {
cout << "Girl 构造函数" << endl;
}
~Girl() {
cout << "~Girl 析构函数" << endl;
}
void setBoyFriend(shared_ptr
this->boyFriend = _boyFriend;
}
private:
shared_ptr
};
void useTrap() {
shared_ptr
shared_ptr
// 单方获得管理 //正解
spBoy->setGirlFriend(spGirl);
spGirl->setBoyFriend(spBoy);
// 陷阱用法(错解)
spBoy->setGirlFriend(spGirl);
spGirl->setBoyFriend(spBoy);
// 此时boy和girl的引用计数都是2
}
int main(void) {
useTrap();
system("pause");
return 0;
}
四。weak_ptr
weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。 同时weak_ptr 没有重载*和->但可以使用 lock 获得一个可用的 shared_ptr 对象。
1.弱指针的使用;
weak_ptr wpGirl_1; // 定义空的弱指针
weak_ptr wpGirl_2(spGirl); // 使用共享指针构造
wpGirl_1 = spGirl; // 允许共享指针赋值给弱指针
2.弱指针也可以获得引用计数;
wpGirl_1.use_count()
3.弱指针不支持 * 和 -> 对指针的访问;
4.在必要的使用可以转换成共享指针 lock();
如:shared_ptr
sp_girl = wpGirl_1.lock();
// 使用完之后,再将共享指针置NULL即可
sp_girl = NULL;
注意weak_ptr指针的expired函数的用法。
expired:判断当前weak_ptr智能指针是否还有托管的对象,有则返回false,无则返回true
如果返回true,等价于 use_count() == 0,即已经没有托管的对象了;当然,可能还有析构函数进行释放内存,但此对象的析构已经临近(或可能已发生)。
最后说一下智能指针的使用陷阱:
1.不要把一个原生指针给多个智能指针管理;
int *x = new int(10);
unique_ptr< int > up1(x);
unique_ptr< int > up2(x);
// 警告! 以上代码使up1 up2指向同一个内存,非常危险或以下形式:
up1.reset(x);//up1 up2指向同一个内存
up2.reset(x);
2.记得使用u.release()的返回值;
在调用u.release()时是不会释放u所指的内存的,这时返回值就是对这块内存的唯一索引,如果没有使用这个返回值释放内存或是保存起来,这块内存就泄漏了.
3.禁止delete 智能指针get 函数返回的指针;
如果我们主动释放掉get 函数获得的指针,那么智能指针内部的指针就变成野指针了,析构时造成重复释放,带来严重后果!
4.禁止用任何类型智能指针get 函数返回的指针去初始化另外一个智能指针!
shared_ptr< int > sp1(new int(10));
// 一个典型的错误用法 shared_ptr< int > sp4(sp1.get());
最后,智能指针虽然使用起来很方便,但是要注意使用智能指针的一些陷阱,否则会造成严重的内存报错或者内存泄露等问题!
本贴为博主亲手整理。如有错误,请评论区指出,一起进步。谢谢大家的浏览.