effect c++ 口诀。
常用条款,写成口诀,记住。知其所以,也要时时使用。
1)习惯c++:
联替const初。
2)构造,复制,析构:
要知默,构赋析。
若不需,明拒绝。
构析不调虚。
异不逃析构。
基析要虚函。
赋值操,每成员。返this引,注自我。
3)资源管理
对象管资源。
copy资管,禁引复移。
资管显隐源,
new,delete,同形式
new资入智,要原操。
4)设计与申明
常引用,返对象
要返引,离开函数要存在(自己添加的条款)
成员变量要私有。
尽量使用外函数。
5)实现
延迟定义,避返内柄。(内柄:内部数据的指针,引用,迭代)
6)继承和面向对象。
区分is和has。
继承非虚函,继承缺省参,绝不重新再定义。
13.以对象管理资源
非常明显的意图,当需要使用堆内存时,必须使用智能指针,接管资源。
认识太浅。。资源不是说堆而已,任何东西都有可能是资源。int是资源,socket也是资源。string。也是资源。就看需不需要管理。
栈里的变量也是资源。重要的是看需不需要管理
1.对于堆中的资源,那么管理类,可以狭义的看成为shared_prt.
2.对于数据库链接。那么管理类,就可以自己写个类,只要在析构上close它就好了,但是根据异常不逃析构原则,需要吞下异常,所以一般会提供close的方法,给用户手动释放资源。而我们的析构是一个保障而已。就算吞下异常,
调用者没有显示close ,也要承担一部分责任。应该早早在close 的时候。捕获异常。
只是,比如数据库链接,应该会做成一个单例模式。
而有些资源,它本身在堆中放着,那么用shared_prt来管理堆中的内存。而它本身里面可能也有资源。所以他还要写析构函数来管理内部资源。
所以对象管理资源这个准则是适合类里面的资源,和类本身相对于内存来说的自身也是资源。很有可能对于一个类的使用和设计。我们这个准则运用了2次。
#include <iostream> #include "stdio.h" #include <memory> #include <unistd.h> #include <thread> #include <vector> #include <exception> using namespace std; class investment { public: investment(int _v):money(_v){} virtual int getit()const =0; ~investment() { try { } catch(exception &ee) { //log std::abort(); } }; protected: int money; }; class socket:public investment { public: socket(int _v):investment(_v){} int getit()const { return investment::money*3; } }; class zaiquan:public investment { public: zaiquan(int _v):investment(_v){} int getit()const { return investment::money*5; } }; void showMoney(const investment& a) { cout<<a.getit()<<endl; } class factory { public: shared_ptr<investment> CreateSocket(int v)//让接口更容易被使用,不被误解。 { return shared_ptr<investment>(new socket(v)); } investment* Createzaiquan(int v)//危险行为. { return new zaiquan(v); } }; int main() { factory investmFactory; shared_ptr<investment> psocket=investmFactory.CreateSocket(3); showMoney(*psocket); shared_ptr<investment> pzaiquan=shared_ptr<investment>(investmFactory.Createzaiquan(4)); showMoney(*pzaiquan); }#include <iostream> #include "stdio.h" #include <memory> #include <unistd.h> #include <thread> #include <vector> #include <exception> using namespace std; class investment { public: investment(int _v):money(_v){} virtual int getit()const =0; ~investment() { try { } catch(exception &ee) { //log std::abort(); } }; protected: int money; }; class socket:public investment { public: socket(int _v):investment(_v){} int getit()const { return investment::money*3; } }; class zaiquan:public investment { public: zaiquan(int _v):investment(_v){} int getit()const { return investment::money*5; } }; void showMoney(const investment& a) { cout<<a.getit()<<endl; } class factory { public: shared_ptr<investment> CreateSocket(int v)//让接口更容易被使用,不被误解。 { return shared_ptr<investment>(new socket(v)); } investment* Createzaiquan(int v)//危险行为. { return new zaiquan(v); } }; int main() { factory investmFactory; shared_ptr<investment> psocket=investmFactory.CreateSocket(3); showMoney(*psocket); shared_ptr<investment> pzaiquan=shared_ptr<investment>(investmFactory.Createzaiquan(4)); showMoney(*pzaiquan); }
14,在资源管理类中小心copy行为。
常见的资源是heap中的资源。一般常用法是用shared_ptr这个类来管理。
而有很多资源并非是内存,可能是锁,可能是SOCKET,DB CONNECT.等等。
堆中的资源,copy行为对于 共享指针是位拷贝。符合堆资源的要求。
而对于其他资源,大部分是禁止COPY 的。比如锁。STL的中管理锁的类有LOCK_GURAD。对于COPY,是禁止的,因为MUTEX,本身就禁止了复制。当然管理类是不可能实现COPY 行为的。
资源管理类对于copy有4种,禁引复移。
禁止:就如lock_gurad,这种管理锁类的资源管理类,对于资源是禁止复制的。
引用计数:这个就是大名鼎鼎的shared_ptr,只复制资源的指针,采用计数来确定是否删除。
复制:代表也是大名鼎鼎的string,由于使用的惯性(习惯会认为是2个不同的字符串,修改一个对其他的没有影响),所以string,采用的是复制,而不是引用计数。
移动,当然也是大名鼎鼎的auto_ptr
#include <iostream> #include "stdio.h" #include <memory> #include <unistd.h> #include <thread> #include <vector> #include <mutex> #include <unistd.h> #include <stdlib.h> using namespace std; mutex mtx; class mylock_guard { public: mylock_guard(mutex& _mtx):mtx(_mtx) { mtx.lock(); } ~mylock_guard() { mtx.unlock(); } private: mylock_guard(const mylock_guard&); mylock_guard& operator=(const mylock_guard&); mutex& mtx; }; void ShowMsg() { //lock_guard<mutex> lck(mtx); mylock_guard lck(mtx); cout<<"1 seconed"<<endl; sleep(1); } int main() { thread t1(ShowMsg); thread t2(ShowMsg); thread t3(ShowMsg); t1.detach(); t2.detach(); t3.detach(); string cmd; cin>>cmd; }
15.在资源管理类中提供对原始资源的访问。
在shared_prt类中。提供了get(),函数支持对原始资源的访问。是属于显示支持。
16,成对使用delete 和new 要采用相同的形式。
new delete. new[] delete[]
不过由于STL的存在,对于基本型。如char ,一般会使用string。string(char* ,int),基本和shared_ptr类似的管理资源。
只是对于copy行为, 智能指针是位拷贝,属于浅拷贝(但有引用计数),而string, 属于深拷贝(无引用计数)。
由于STL的存在,很少会直接使用 new 和 delete .除非对性能比较敏感,比如char,可能就不使用string.而直接用CHAR*来做缓存类。那么就必须手动 new 和 delete.
17,以独立语句将newd 的资源放入资源管理类。
这个如何理解,就是根本上,NEW了资源,第二部就是给资源管理类。
我们细细想下,进入一个函数,栈空间已经开辟,所以资源管理类是不会出错的。比如是共享指针。
那么可能异常的只有NEW资源,当NEW资源失败,那也没有造成内存泄露。
只要NEW 正常。那么共享指针就接管了资源。行为完全正确。
而假如 initme(shared_ptr<> a, int)
调用时,却采取 initme(shared_ptr<t>(new T()), foo());
由于编译器的问题。很可能,foo() 的执行会插在NEW之后,而这个时候,资源管理类还没有接管资源哦, 如果 FOO出错。那么NEW 的资源就泄露了。
所以正确的行为是
shared_ptr<t> pt (new T());
initme(pt, foo());
18,设计与申明。
18. 让接口,简单,不易误解。
比如如果返回堆中对象,可以是指针或者智能指针,返回栈中队形,则必须是对象。
而返回堆中对象,可以优先选择智能指针,这样迫使客户使用时,建立智能指针对象来接受。避免客户的疑惑和忘记删除。但是google 的 protobuf ,却为了性能,使用了自己的资源管理类,返回的是指针。
19.设计class 犹如设计type.
任务太艰巨,只能实践中检验,先放下。
20,以const by ref替换by value.
1,性能的考虑,BY VALUE,需要COPY构造和析构,这还不包括里面是否有对象需要构造和析构。
2.继承,如果参数是基类,那么传递派生类。会进行切割。
3.可以BY VALUE传递的是内置类型,迭代器和函数对象。
迭代器在VECTOR中,其实就是元素指针的别名。而函数对象,一般会比较少而且是内置类型的内部成员,
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步