C++学习笔记

C++学习笔记03
栈对象:仅在其定义的程序块运行时才存在(普通的函数定义什么的)
堆对象:动态分配的对象,可随时建立或删除的对象(new)

动态内存的管理是由一对运算符完成:
new(在动态内存中为对象分配空间并返回一个指向该对象的指针)
delete(结束一个动态对象的指针,销毁该对象,释放内存)
(如果new了一个对象,忘记delete了会造成内存泄漏;delete了还调用就会崩溃)

两种智能指针:
shared_ptr:允许多个指针指向同一个对象
unique_ptr:"独占"所有对象(会自动释放内存)
智能指针也是一种模板,默认初始化是空指针
shared_ptr p1; if(p1 && p1->empty()){*p1 = “ji”};//表示判断指针是否为空指针且指向的是否是空字符串
(用count对指向的对象进行计数,当有指针指向该内存时,智能指针的count加1,当count为0时,指向的对象内存就销毁了)
swap(p,q)

shared_ptr独有的操作:
make_shared(args): 返回一个智能指针,指向动态分配的类型为T的对象,使用args初始化对象,(即args是构造T类型所需的参数)
shared_ptr p(q): p是q的拷贝,这样操作会递增q的计数器
p = q;会减少p的计数,若p原来指向一个对象(只有p指向),赋值后p指向q,且原对象回收

我们希望b2 = b1;是b1销毁,然后b2指向b1的对象,所有b1指向的对象不销毁(达成共享,而不是简单拷贝一份给b2,然后b1的对象销毁)
用new分配const对象是可以的,const string *p = new const string;
delete只删指针,不能删非动态的指针(空的可以),如果有另一个指针p指向new出来的指针q,new的那个q被delete了,p会成为野指针,且还不能delete p
int *q(new int(42));//里面动态分配了一块空间,生成一个指针q指向那个对象 auto p = q;delete q;//p,q都无效

shared_ptr p1(new int(42));//表示p指向一个值为42的int,不是赋值
shared_ptr p2 = new int(42);//错误,不能隐式转化为shared_ptr

定义和改变shared_ptr的其他方法:
shared_ptr p(q)//p指向q的对象,q必须是指向new分配的内存
shared_ptr p(u)//p接管unique_ptr指针的对象所有权,u置为空
shared_ptr p(q,d)//p接管q指向对象,p能调用对象d来代替delete
shared_ptr p(p2,d)//p是shared_ptr p2的拷贝,p将调用d代替delete
p.reset()//若p是唯一指向其对象的shared_ptr,reset会释放此对象
p.reset(q)//***********************************,p指向q

向unique_ptr传递删除器(和智能指针一样的但不用delete的函数):
void f(dest &d){
cone c = ct(&d);//打开链接

unique_ptr<cone,decltype(end_cone)*> p(&c , end_cone);
}
普通指针转内置指针:int *p = new int(42); shared_ptr p1§;/int a = 10;shared_ptr p(a);
内置指针转普通:int p = p1.get();
(用shared_ptr p生成的就是内置指针,用make_shared p生成的就是智能指针)

int *x(new int(2012));//危险,x是一个普通指针
不要混合使用普通指针和智能指针,因为智能指针离开函数作用域时,会使指向的对象count-1,特别是如果显式将普通指针构造成智能指针【shared_ptr(x)】,如果count(只有智能指针才能算来计数)为0了,原来普通指针指向对象也会销毁,普通指针就成野指针了
永远不要用get初始化另一个智能指针或为另一个智能指针赋值,因为两边的智能指针计数是不同的
if(!p.unique())
p.reset(new string(*p));//若p指向的对象不是unique的,还有别的指针指向这个对象,就不要乱改,就生成一个新的对象拷贝原对象,且这个拷贝对象只有p指向,所以可以自己随便改
*p += val;

unique_ptr u1;//空,默认用delete
unique_ptr<T,D> u2;//调用对象D进行释放
unique_ptr<T,D> u(d);//空,用类型为D的d代替delete
u = nullptr//释放u指向的对象,u置空
p = u.release()//u转移指针控制权给p,u置空,通常用来给智能指针赋值,注意如果转移给了普通指针,要自行delete
u.reset()//释放对象
u.reset(q)//q是内置指针,释放u原来的对象,然后指向q

unique_ptr p(new int(25));
unique_ptr p(p2);//错误,unique_ptr不支持拷贝,也不支持赋值(如将p赋给同为unique的p3)
unique_ptr p2(p1.release);//p1是unique的

可以拷贝或赋值给一个即将销毁的unique_ptr,比如某些函数的内部新建的unique_ptr,最后要用这个指针返回出去,而这样指针在函数执行完就要释放了

weak_ptr:绑定到shared_ptr,不会改变shared_ptr的引用计数
weak_ptr w(sp)或者w = sp;//与shared_ptr的sp指向同一对象
w.use_count();//返回共享对象有多少个shared_ptr
w.expired();//若w.use_count()返回0,则返回true
w.lock()//若w.expired返回true,则返回一个空shared_ptr,否则返回一个指向w对象的shared_ptr
不能用weak_ptr直接访问对象,要先用lock询问

new和数组(动态数组并不是数组类型,通过get_size()分配大小) int *p = new int[get_size()];//get_size()个未初始化的int
int *p = new int10;//10个默认初始化为0的int
int *p = new int[10]{1,2,3,4,5,6,7,8,9,0};//用列表初始化
char arr[0];//错误,不能定义长度为0的数组 char *cp = new char[0];//正确,但是不能解引用
释放动态数组:delete[] pa;//中括号写前面

指向数组的unique_ptr:
unique_ptr<T[]> u; unique_ptr<T[]> u§;//u指向内置指针p的动态分配的数组
shared_ptr不直接支持管理动态数组

allocator类:将内存分配和对象构造分离
allocator a
a.allocate(n);//分配n个未构造的内存,不能解引用的
a.construct(p,args)//p必须是一个类型为T*的指针,args是传递给类型为T的构造函数
a.destroy§//对p指向的对象执行析构函数
a.deallocate(p,n)//释放从p开始的n个内存
uninitialized_copy(b,e,b2)//从迭代器b和e的范围拷贝到b2指向的未构造内存中
uninitialized_fill(b,e,t)//在迭代器b和e之间范围创建对象值为t的对象

=delete(为了不允许拷贝) =default
如果定义拷贝操作表现得像一个值,类的每一个对象都应该拥有一份自己的拷贝,再回收内存时就可以回收拷贝的那份内存,避免多次回收同一份内存
如果一个对象赋予它自己,赋值运算符必须能正常工作,大多赋值运算符组合了析构函数和拷贝构造函数的工作
A& A::operator=(const A &rhs){
auto newp = new string(*rhs.ps);
delete ps;
ps = newp;
i = rhs.i;
return *this;
}
如果定义拷贝操作表现得像指针,在类加一个类似引用计数的参数use(分配动态内存),这样在重载复制运算符和析构函数都要考虑use的增减,当其为o时就要delete对象和deleteuse

strVec
对象移动(没有新建):&&获得右值引用
左值持久,右值短暂(临时变量)/左值获得身份,右值获得值
int &i = j;//左值引用绑定左值 int &&i = j;//错误,右值引用不能绑定左值
int &i = j42;//错误左值不能引用右值(临时变量) 可将一个const引用绑定到右值:const int &r3= i52; int &&r2 = i*85;//正确
移动构造函数不分配新内存
只有当一个类没有定义任何自己版本的拷贝控制成员,且他们的所有数据陈冠都能移动构造或移动赋值时,编译器才会为他们合成移动构造函数或移动赋值运算符
如果没有移动构造函数,右值也被拷贝
v1 = v2;//v2是左值,使用拷贝赋值 v2= getVec(cin);//假设getVec(cin)是右值1,使用移动赋值

不能重载 :: .* . ?=
具有对称性的运算符应该重载成普通的非成员函数
重载输入运算符必须处理输入有可能失败的情况,输出运算符不需要
在重载左++和右++时,右++在形参中输入int以区别左++
lamdba是函数对象,有其相应的类

static_cast(强制,不安全)和dynamic_cast都是将父类转子类dynamic<子类>(父类)
dynamic_cast类型转换时必需要有virtual函数支持
动态绑定:虚函数被调用时,编译时不清楚调用哪个版本,执行才知道,好处是照顾到未来增加的代码,而且在main执行时不用一个个.函数,缺点是
会影响执行效率
指针是有动态性质的

函数调用运算符重载(仿函数):void operator()(string s){cout << s << endl};
A a; a(“hi”);//对象加括号就会调用了
虚函数:在基类加virtual,希望派生类自定义自己的版本
动态绑定:用同一段代码分别处理派生类和基类
如double print_data(ostream &os, const Quote& item){double ret = item.net_price();…}//输入形参在函数定义时是写基类Quote,不过调用时也可以用派生类,然后具体怎么调用要看运行时才能知道
基类通常都定义一个虚析构函数virtual ~Quote()=default;
派生类class Bulk_quote:public Quote{}
派生类通常覆盖它继承的虚函数
用基类指针p指向派生类pr或者用基类对象引用派生类是可以的,相当于对于p的接口只会有派生类从基类继承的那些,不能用基类赋给派生类
派生类必须用基类的构造函数来初始化派生类Bulk_quote(const std::string& book, double p, std::size_t qty, double disc):Quote(book, p),min_qty(qty), discount(disc){}
派生类可以访问基类的公共成员和受保护成员,静态成员不属于任何一个对象,可以通过 基类::函数,派生类::函数,对象.函数,函数(this指针)调用
派生类的声明class Bulk_quote;//不需要加什么public Quote;
class Last final:Base{};//在Last后加final表示Last这个类不能作为基类再派生

友元关系不能继承
如果一个名字找不到定义,就往外层基类去找
派生类的同名函数会隐藏基类的//除非显式调用,不然那个函数就是被隐藏了,虚函数就没有隐藏的概念,要么覆盖要么继承

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜以冀北

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值