赞
踩
// 智能指针单例 // 饿汉式单例的实现 #ifndef C_SINGLETON_H #define C_SINGLETON_H #include<iostream> #include<memory> using namespace std; class CSingleton { private: CSingleton(){ cout << "单例对象创建!" << endl; }; CSingleton(const CSingleton &); CSingleton& operator=(const CSingleton &); ~CSingleton(){ cout << "单例对象销毁!" << endl; }; static void DestroyInstance(CSingleton *){ cout << "在这里销毁单例对象!" << endl; };//注意这里 static shared_ptr<CSingleton> sington_; public: static CSingleton& getInsance(){ if (!sington_) { std::lock_guard<std::mutex> lock(mutex_); if (nullptr == sington_) { sington_ = std::make_shared<CSingleton>(); // 用make_shared效率更高,代码更简洁 } } return sington_; } }; #endif //主文件,用于测试用例的生成 #include<iostream> #include<memory> #include"CSingleton.h" using namespace std; shared_ptr<CSingleton> CSingleton::myInstance(new CSingleton(), CSingleton::DestroyInstance); int main() { shared_ptr<CSingleton> ct1 = CSingleton::getInstance(); shared_ptr<CSingleton> ct2 = CSingleton::getInstance(); shared_ptr<CSingleton> ct3 = CSingleton::getInstance(); return 0; }
#include<iostream> #include<memory> using namespace std; class CSingleton { private: CSingleton(){ cout << "单例对象创建!" << endl; }; CSingleton(const CSingleton &); CSingleton& operator=(const CSingleton &); ~CSingleton(){ cout << "单例对象销毁!" << endl; }; static void DestroyInstance(CSingleton *){ cout << "在这里销毁单例对象!" << endl; };//注意这里 static unique_ptr<CSingleton> sington_; public: static CSingleton& getInsance(){ static std::once_flag flag; std::call_once(flag, [&]() { sington_.reset(new T); }); return *sington_; } }; std::unique_ptr<CSingleton> CSingleton::sington_;
Receiver * pReceiver = new Receiver();
ConcreteCommand * pCommand = new ConcreteCommand(pReceiver);
Invoker * pInvoker = new Invoker(pCommand);
pInvoker->call();
Strategy * s1 = new ConcreteStrategyA();
Context * cxt = new Context();
cxt->setStrategy(s1);
cxt->algorithm();
Strategy *s2 = new ConcreteStrategyB();
cxt->setStrategy(s2);
cxt->algorithm();
void swap(int &a,int &b); swap(x,y);//引用调用,相当于地址传递,会改变实参。
int &ri=i; ri=10;(equivalent to i =10)//从而ri就是i的别名。引用必须在定义时就初始化,且以后不可指向其他对象。
struct stu nem; 可以写成 stu nem
int add(int x=5, int y=6) { return x+y;} add(); add(10,20);add(10)
结果为11,30,16
.int add(int x, int y) ; float add(folat x , float y); 以及 int add(int x, int y); int add(int x,int y, int z);
都满足。void larger(const int &a, const int &b)
A const a(3,4)
,常对象必须进行初始化,而且不能被更新,且常对象只能调用它的常成员函数常成员函数:(语法:类型 函数名(参数表)const)
常成员函数可以访问但不能更新(修改)对象的数据成员,也不能调用该类中没有用const修饰的成员函数。(这样就保证了常成员函数绝对不会更改数据成员的值)。比如显示函数等就可以使用常成员函数。const int a;
类的常数据成员只能通过初始化列表来获得初始值。reinterpret_cast<指针类型>(表达式)
运算符允许强制转换任何指针类型,其限制是如果要转换的指针类型声明为const,就不能把它转换为不是const的类型。例 long* pnumbe= reinterpret_cast<long*>(pvalue);
这是一种最不安全的转换,应尽量不使用。假如将A对象转换为某个类型,则当再次需要使用A时,A已经不同了,以至于不能用于类型的原来目的,除非再次把它转换回来。static_cast<类型>(表达式)
把表达式转换为给定类型, 与C类似,int,double,及基本类型的指针的转换等。
const_cast<类型>(表达式)
从const转换成非const或从volatile转换为非volatile。四种类型转换中,只有它能实现这个功能。dynamic_cast<type-id>(expression)
只能应用于多态类类型的指针和引用,即至少包含一个虚函数的类类型,也即type-id和expression都必须包含虚函数。该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *
;如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
type*
类型的情况有三种:
carton* pcarton = dynamic_cast<carton*>(pbox)
if(pcarton != nullptr) pcarton->surface();
对于引用,由于没有诸如空引用的情况,这就需要采用捕获bad_cast异常来了解类型转换是否成功。try{…}catch(bad_cast&){…}
class Father { public: virtual void print(){cout<<"I'm Father"<<endl;} void fantastic(){cout<<"father fantastic"<<endl;} }; class Son:public Father { public: void print(){cout<<"I'm Son"<<endl;} void fantastic(){cout<<"son fantastic"<<endl;} }; int main() { Son son; Father father; #if 0 Father *pFather = &father; 如果运行这个,则转化会失败,即pSon为空 #else Father *pFather = &son; #endif pFather->print(); Son *pSon = dynamic_cast<Son*>(pFather); if(pSon!= NULL) { pSon->print(); pSon->fantastic(); } return 0; }
friend float fDist(Point &a,Point &b)
firend class A;
声明,类型说明符 类名::*指针名
;赋值,指针名=& 类名::数据成员名
;引用,对象名.*类成员指针名或对象指针名->*类成员指针名
声明,类型说明符(类名::*指针名)(参数表)
;赋值,指针名=类名::函数成员名
;调用,(对象名.*类成员指针名)(参数表)或(对象指针名->*类成员指针名)(参数表)
int *const=&Point::countP;
(这里countP必须是公有数据) ,void(*gc)()=Point::GetC;
类外指针只能指向公有成员。 int *point; point=new int(2); /*动态分配用于存放int类型数据的内存空间,并将初值2存入该空间,然后将首地址赋给指针point*/
Point *Ptr=new Point[2]; delete [ ] Ptr; int *p=new int[10]; delete [ ]p
float (*cp)[25][10]; cp=new float [30][25][10];
则cp[i][j][k]
等价于采用数组名[i][j][k]
调用一个三维数组[30][25][10]
的元素。new operator
, operator new
, placement new
. new opeartor 即是我们熟悉的那个new,new operator
时,它做了三件事:
int a[10]; X *xp = new(a) X(47) // X at location a xp->X::~();
简化版例子,如需使用,详见C++编程思想p685或查找资料opeartor new
处理)失败时,opeartor new
首先会调用一个错误处理函数new_handler
进行相应的处理,如果处理成功则返回地址,否则转而去调用另外一个new_handler
,然后继续前面的过程,直到new_handler中调用exit()或类似的函数,使程序结束或者new_handler
中抛出异常,std::bad_alloc
是C++标准中规定的标准行为,所以推荐使用try{p=new int[SIZE];}catch(std:bad_alloc){……}
的处理方式。try{
int*pStr=new string[SIZE];
//processing codes
}catch(const ::std::bad_alloc &e){
/*processing codes*/
return-1;
} // 不要使用C中的if(pStr == NULL), 可以使用 int *pSTR = (::std::nothrow)new int[SIZE];
void*
类型指针进行delete操作, 要注意这将可能成为一个程序错误, 除非指针所指的内容是非常简单的, 因为它将不执行析构函数。如果在程序中发现内存丢失的情况, 那么就搜索所有的delete语句并检查被删除指针的类型。 如果是void*
类型, 则可能发现了引起内存丢失的某个因素( 因为C++还有很多其他的引起内存丢失的因素)类名::标识符
。静态数据成员应该通过非内联函数来访问,而且访问静态数据成员的函数,其函数体应该与静态成员的初始化写在同一个源文件程序中。在类的定义中仅仅对静态数据成员进行声明,必须在文件作用域的某个地方使用类名限定对静态数据成员进行定义,这时也可以初始化。void Point::f(Point a){cout<<x; /*错误*/ cout<<a.x; /*正确*/}
,但最好不要这样使用,因为如果需要这样的话普通函数更好,没必要使用静态函数 ,。既可以使用类名,也可以使用对象名来调用静态成员函数(Point::GetC( ) )
C::C(C&c1):B(c1){…}
(这里B中放c1是由于类型兼容原则,可以由派生类的引用去初始化基类的引用)基类名::成员名
; 和针对函数成员的 基类名::成员名(参数表)
, 无法通过子类无法访问(编译器会报错),就好像子类中没有该成员一样。Instructment
和派生类Wind
中有一个同名函数比如play()
,则当Wind w; Instructment *ip = &w; ip->play();
只能调用Instructment::play()
,而不是Wind::play()
. 且只能访问从基类继承的成员。fun()
,则B0、B1两者有一个同样的函数fun(),则C中有两个fun()函数,C在使用fun时必须这样使用,C c1; c1.B0::fun()
;class 派生类名:virtual 继承方式 基类名
),设置是将B0,B1以(class 派生类名:virtual 继承方式 基类名)
从继承的,然后C以继承方式 B0, 继承方式 B1
来继承。这样从不同的路径继承过来的同名数据成员在内存中就只有一个拷贝,同一个函数名也只有一个映射。建立一个对象时,如果这个对象含有从虚基类继承的成员,则虚基类的成员是由最远派生类的构造函数通过调用虚基类的构造函数进行初始化,,而且,只有最远派生类的构造函数会调用类的构造函数,该派生类的其他基类(B0、B1)都不会。class Father { public: virtual void print(){cout<<"I'm Father"<<endl;} }; class Son:public Father { private: void print(){cout<<"I'm Son"<<endl;} }; int main() { Son son; Father *pFather = &son; pFather->print(); //不会报错 return 0; } /*说明:虚函数编译时的访问权限仅仅是和调用者(指针、引用)的类型有关,编译器只要知道这个类型有一个可以访问的虚函数就行了,并不查看具体对象类型中该虚函数的访问权限*/
class Father { public: virtual void print(){cout<<"I'm Father"<<endl;} virtual void print(int val) { cout <<"I'm Father Val:" <<val <<endl;} }; class Son:public Father { public: void print(){cout<<"I'm Son"<<endl;} }; int main() { Son son; Father *pFather = &son; pFather->print(); //son.print(1) 不注释的话编译器会报错 ,父类的其它同名函数被隐藏 return 0; }
virtua 函数类型 函数名 (参数表)= 0
;template <模板参数表> 类型名 类名<T>::函数名(参数表)
形式,例:template <typename T> Array <T> :: Array(const Array& theArray)
, Array 是一个类名。模板类的成员函数必须是函数模板。声明Array类时 格式:Array<double> D ,或Array<double>D(2)
,后一种声明时还初始化了。无非将Array变为Array<double>
,其他声明也类似。但声明之后,就可以像正常类使用了,比如这里D要调用一个成员函数,直接D.成员函数名
。template <class T> T max(T a , T b)
(class 或typename均可); 和普通函数一样,可以先声明后定义,也可以立即定义。可以显式指定模板参数(隐式就是编译器根据实参来确定返回类型),比如当a为int,b为long时编译器就不能生成合适的函数,这时可以显式指定模板参数:max<long>(a,b)
; 如果max(&a,&b)会出得到不想要的结果,可通过模板说明来处理(略)或者重新写一个针对max(&a,&b)的函数,在使用时会优于重载函数。带有多个参数的模版:template<class Treturn, class Targ> T return max (Targ, Targ b);
因为编译器不能推断返回值的类型T return
,所以必须指定该类型。max<int>(1.4, 2.4);
还可以指定T return
和T arg
, max<double,double>(1.4, 3.5)
. 函数模板中参数的定义是非常重要的,如果返回类型定义为第二个参数,函数调用中就必须总是指定两个参数。等号左边的就是左值,等号右边的就是右值, 这个值能不能放等号左边,能放就是左值,否则就是右值。另一种方法是可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值。比如a = b+c, &a是允许的操作,但&(b+c)这样的操作则不会通过编译,则a是一个左值,b+c是一个右值。
在C++11 中右值分两种,一个是将亡值,一个是纯右值。其中纯右值就是C++98中的概念。而将亡值则是C++11新增的跟右值引用相关的表达式,这样的表达式通常是将要被移动的对象(移为他用),比如返回右值引用T&&的函数返回值、std::move的返回值,或者转化为T&&的类型转换函数的返回值。
左值引用和右值引用
T&& a = ReturnRvalue();
这个表达式中,ReturnRvalue()返回的右值在表达式结束后,其生命也就终结了,而通过右值引用的声明,该右值又“重获新生”,其生命周期将与右值引用类型变量a的生命期一样。相比与如下的声明方式T b = ReturnRvalue()
前者少一次对象的析构及一次对象的构造。bool& result = true
)不能绑定到右值,编译器会报错,但常量的左值引用(如const bool& result = true
)可以绑定到右值,且会延长右值的生命期。引用折叠
区分万能引用和右值引用
auto&& var
也是万能引用,但Widgt&& var
和template<typename T> void f(Widget&& param)
都是右值引用,void f(Widget&& param)
也是右值引用在C++11中,我们用左值去初始化一个对象或为一个已有对象赋值时,会调用拷贝构造函数或拷贝赋值运算符来拷贝资源(所谓资源,就是指new出来的东西),而当我们用一个右值(包括纯右值和将亡值)来初始化或赋值时,会调用移动构造函数或移动赋值运算符来移动资源,从而避免拷贝,提高效率
C++11新增移动构造函数和赋值构造函数
ClassName(ClassName&& T); // 这里不能加cosnt
ClassName& operator(ClassName &&T);
编译器生成默认移动构造函数或赋值构造函数的条件极其苛刻,如下,这里的声明的意思就是显示声明并实现了:
C++11中还增加了成员函数的引用修饰词,示例代码如下:
// 对于如下定义的代码: class Widget{ public: using DataType = std::vector<double>; DataType& data() { return values; } private: DataType values; }; // 如果按如下方法调用, 这里就是vals1就是拷贝构造的 Widget w; auto vals1 = w.data(); // 但如果按照如下方式调用,则没必要进行拷贝,比较好的做法是移动而非复制 auto vals2 = makeWidget().data(); /* 可以使用引用修饰词来解决, 最终版本如下, 调用哪个取决于调用的对象是左值还是右值,这里makeWidget().data()中,makeWidget()是一个右值,调用右值版本,而w.data()中,w是左值,调用左值版本。*/ class Widget{ class Widget{ public: using DataType = std::vector<double>; DataType& data() & //对于左值Widgets类型,返回左值 { return values; } DataType& data() && //对于右值Widgets类型,返回右值 { return std::move(values); } private: DataType values; };
*ptr
获取指针内容了,会运行错误。std::move
**不能将常量转化为右值class Test { int * arr{nullptr}; public: Test():arr(new int[5000]{1,2,3,4}) { cout << "default constructor" << endl; } Test(const Test & t) { cout << "copy constructor" << endl; if (arr == nullptr) arr = new int[5000]; memcpy(arr, t.arr, 5000*sizeof(int)); } /*不能使用const Test && t, 因为需要对t进行修改*/ Test(Test && t): arr(t.arr) { cout << "move constructor" << endl; t.arr = nullptr; } ~Test(){ cout << "destructor" << endl; delete [] arr; } }; Test createTest() { return Test(); } int main() { Test t(createTest()); } /* 我们会发现,打印的结果为: default constructor move constructor destructor move constructor destructor destructor */ /*gcc是一个丧心病狂的编译器,他会强制进行RVO返回值优化。如果你不做任何设置直接用GCC编译运行上面的代码,你将看到的是: default constructor 这个时候不要怀疑写错了。请直接在gcc后面添加编译参数-fno-elide-constructors*/ /*如果这里没有Test(Test && t) 函数,则就要进行繁杂的深拷贝*/
template<class T>
void swap(T& a, T& b)
{
T tmp(move(a));
a = move(b);
b= move(tmp);
}
template <typename T> void IamForwarding(T t){ IrunCodeActually(t); } /*这里就希望传入IamForwarding的是左值对象,IrunCodeActually就能获得左值对象,传入是右值对象,就能获得右值对象,而不产生额外的开销,就好像转发者不存在一样。*/ /*我们考虑一个例子*/ template <typename T> void func(T t) { cout << "in func" << endl; } template <typename T> void relay(T&& t) { cout << "in relay" << endl; func(t); } int main() { relay(Test()); } /*那么现在我们来运行一下这个程序,我们会看到,结果与我们预想的似乎并不相同: default constructor in relay copy constructor in func destructor destructor 我们看到,在relay当中转发的时候,调用了复制构造函数,也就是说编译器认为这个参数t并不是一个右值,而是左值。这个的原因已经在上一节将结果了,因为它有一个名字。那么如果我们想要实现我们所说的,如果传进来的参数是一个左值,则将它作为左值转发给下一个函数;如果它是右值,则将其作为右值转发给下一个函数,我们应该怎么做呢? */
TR
有可能是类型T的引用或是类型T,即可能是T&,T&&或T的别名, 假定函数test(TR x), 当传递给函数的参数有可能是T、T& 、T&&
, 则x的实际类型是什么呢? 实际上,会发生引用折叠,这个规则是总是优先将其折叠为左值引用。而模板对类型的推导规则就比较简单,当转发函数的实参是类型X的一个左值引用,那么模板参数被推导为X&,而转发函数的实参是类型X的一个右值引用的话,那么模板的参数就被推导为X&&类型。template<class A> void G(A &&a) { F(forward<A>(a)); } template <typename T> void IamForwarding(T&& t){ IrunCodeActually(forward<T>(t)); } // forward的作用将一个对象转发给另一个对象,同时保留该对象的左值性或右值性。 // 与std::move()相区别的是,move()会无条件的将一个参数转换成右值,而forward()则会保留参数的左右值类型。 // forward函数模板源码如下: template <class _Ty> _NODISCARD constexpr _Ty&& forward( remove_reference_t<_Ty>& _Arg) noexcept { // forward an lvalue as either an lvalue or an rvalue return static_cast<_Ty&&>(_Arg); } template <class _Ty> _NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept { // forward an rvalue as an rvalue static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call"); return static_cast<_Ty&&>(_Arg); } /* 第一个forward function template中,模板参数是一个左值。当调用该函数的实参为左值时,这个template就将forward an lvalue as lvalue.当调用该函数的实参为右值时,这个template就将forward an lvalue as rvalue. 第二个forward function template中,模板参数是一个右值。当调用该函数的实参为左值时,这个template就将执行static_assert这一句,不进行转换。当调用该函数的实参为右值时,这个template就将forward an rvalue as rvalue. */
//所以我们的代码应该是这样: template <typename T> void func(T t) { cout << "in func " << endl; } template <typename T> void relay(T&& t) { cout << "in relay " << endl; func(std::forward<T>(t)); } /*现在运行的结果就成为了: default constructor in relay move constructor in func destructor destructor 而如果我们的调用方法变成:*/ int main() { Test t; relay(t); } /*那么输出就会变成: default constructor in relay copy constructor in func destructor destructor 完美地实现了我们所要的转发效果。*/
tmplate<typename T>
void f(ParamType param);
f(expr)
template<typename T>
void f(T& param); // param是个引用, 也有可能是 T* param
/*
又声明了下列变量
int x = 27;
const int cx = x;
const int& rx = x;
在各次调用中,对param和T的类型推导结果如下:
f(x); // T的类别是int, param的类别是int&
f(cx); // T的类别是const int, param 的类别是 const int&
f(rx); // T的类别是const int , param 的类别是 const int&
*/
{A,B,C,...}
),则编译器就会选用这种解释。只有在找不到任何办法把大括号初始化物中的实参转换为std::initializer_list模板中的型别时,编译器才会退而去检查普通的重载协议。
std::vector<int> v{10,0}
, 如果是小括号,则表示10个元素,每个都是0, 但由于是大括号,则优先推导为std::initializer_list,结果就是两个元素,一个为10,一个为0.//示例代码如下: int i = 4; int arr[5] = {0}; int *ptr = arr; struct s{ double d; }s; void Overloaded(int); void Overloaded(char); int&& rvalRef(); const bool Func(int); // 规则1 decltype(arr) var1; // int[5], 标记符表达式 decltype(ptr) var2; // int * , 标记符表达式 decltype(s.d) var4; // double, 成员访问表达式 decltype(Overloaded) var5 // 无法编译通过 // 规则2, 将亡值 decltype(RvalRef()) var6 = 1; // int && // 规则3 decltype(true?i:i) var7 = i; // int&, 返回i的左值 decltype((i)) var8 = i; // int&, 带圆括号的左值 decltype(++i) var9 = i; // int&, ++i 返回i的左值 decltype(arr[3]) var10 = i; // int&, [] 操作返回左值 decltype(*ptr) var11 = i; // int&, *操作返回左值 decltype("str") var12 = "str"; //const char(&)[90], 字符串字面常量为左值 // 规则4 decltype(1) var13; // int, 除字符串常量外,其它常量为右值 decltype(i++) var14; // int , i++返回右值 decltype((Func(1))) // const bool, 圆括号可以忽略
// 示例如下: /* 此处使用了 C++11 的尾随返 回类型技术,即函数的返回值类型在函数参数之后声明(“->”后边)。尾随返回类型的一个优势 是在定义返回值类型的时候使用函数参数。例如在函数 authAndAccess 中,我们使用 了 c 和 i 定义返回值类型。在传统的方式下,我们在函数名前面声明返回值类 型, c 和 i 是得不到的,因为此时 c 和 i 还没被声明。*/ template<typename Container, typename Index> // 有缺陷,后面再修改 auto authAndAccess(Container& c, Index i)-> decltype(c[i]) { authenticateUser(); return c[i]; } /*对使用 auto 来表明函数返回类型的情况,编译器使用模板类型推导。但是这 样是回产生问题的。正如我们所讨论的,对绝大部分对象类型为 T 的容器, [] 操作子返回 的类型是 &T , 然而条款一提到,在模板类型推导的过程中,初始表达式的引用会被忽略。*/ /*为了让 authAndAccess 按照我们的预期工作,我们需要为它的返回值使用 decltype 类型推 导,即指定 authAndAccess 要返回的类型正是表达式 c[i] 的返回类*/ // 正确的写法如下: template<typename Container, typename Index> // final C++11 version auto authAndAccess(Container&& c, Index i) -> decltype(std::forward<Container>(c)[i]) { authenticateUser(); return std::forward<Container>(c)[i]; } /* C++ 的拥护者们预期 到在某种情况下有使用 decltype 类型推导规则的需求,并将这个功能在 C++14 中通 过 decltype(auto) 实现。这使这对原本的冤家(decltype 和 auto ) 在一起完美地发挥作 用: auto 指定需要推导的类型, decltype 表明在推导的过程中使用 decltype 推导规则*/ template<typename Container, typename Index> // final C++14 version decltype(auto) authAndAccess(Container&& c, Index i) { authenticateUser(); return std::forward<Container>(c)[i]; }
int main(){
vector<int> v = {1, 2, 3, 4, 5};
for(auto i=v.begin(); i != vec.end(); ++i){
cout<<*i<<endl;
}
for(auto e:v){
cout<< e <<endl; // 这里是e,而不是*e
}
return 0;
}
[capture list] (parameter list) -> return type { function body }
捕获列表,参数列表(可省略),返回值类型(可省略),函数体。bool cmp(int a, int b){ return a < b; } int main(){ vector<int> myvec{ 3, 2, 5, 7, 3, 2 }; vector<int> lbvec(myvec); sort(myvec.begin(), myvec.end(), cmp); // 旧式做法 cout << "predicate function:" << endl; for (int it : myvec) cout << it << ' '; cout << endl; sort(lbvec.begin(), lbvec.end(), [](int a, int b) -> bool { return a < b; }); // Lambda表达式 cout << "lambda expression:" << endl; for (int it : lbvec) cout << it << ' '; return 0; } /*在C++11之前,我们使用STL的sort函数,需要提供一个谓词函数。如果使用C++11的Lambda表达式,我们只需要传入一个匿名函数即可,方便简洁,而且代码的可读性也比旧式的做法好多了。*/ // 另外一个例子: /* find_if 的作用: Returns an iterator to the first element in the range [first,last) for which pred returns true. If no such element is found, the function returns last.*/ int main(){ vector<int> vec={1, 3, 6, 7, 7, 8}; int val = 7; std::vector<int>::iterator iter = find_if(vec.begin(), vec.end(), [val](int i){ return i == val; }) // use iter to ... }
auto_ptr
, C++11摒弃了这种方案,提供了另外两种方案unique_ptr and shared_ptr
.
shared_ptr
(本质是引用计数)。unique_ptr
void runGame(){
std::auto_ptr<Monster> monster1(new Monster());//monster1 指向 一个怪物
monster1->doSomething();//怪物做某种事
std::auto_ptr<Monster> monster2 = monster1;//转移指针
monster2->doSomething();//怪物做某种事
monster1->doSomething();//Oops!monster1智能指针指向了nullptr,运行期崩溃。
}
int main() { unique_ptr<int> up1(new int(11)); // unique_ptr<int> up2 = up1 //错误!!! 无法通过编译 cout << *up1 <<endl; // 11 unique_ptr<int> up3 = move(up1); //现在up3是数据唯一的unique_ptr智能指针 cout<<*up3<<endl // 11 // cout<<*up1<<endl // 运行时错误 up3.reset(); // 显式释放内存 up1.reset(); // 不会导致运行错误 } int main() { unique_ptr<int> up2(new int(22)); up2.reset(new int(23)); // 释放指向22的内存,并转为指向23 cout<<*up2<<endl; // 23 up2.reset(); // 释放23 if(!up2) {printf("up2 is empty()\n");} // 输出up2 is empty() }
#include <iostream> #include <memory> #include <vector> using namespace std; class ClassB; 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; // classA和classB的资源不会得到释放,因为最终引用计数为1 }
int main() {
shared_ptr<int> sp(new int(5));
cout << "创建前sp的引用计数:" << sp.use_count() << endl; // use_count = 1
weak_ptr<int> wp(sp);
cout << "创建后sp的引用计数:" << sp.use_count() << endl; // use_count = 1
}
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; } }
*
操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。class ClassB; 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; // 函数结束,思考一下:spa和spb会释放资源么? }
auto_ptr
或shared_ptr
(new[]也不能); 不使用new 或new[]分配内存时,不能使用unique_ptr
make_unique
和make_shared
, 示例auto spws(std::make_shared<Widget>());
因为只会引发一次内存分配,控制块和对象的构造函数一起分配,并且书写上更简单。但是make函数无法自定义析构函数
fut.get()
即可获取,示例如下:int doAsycWork();
std::thread t(doAsycWOrk); // 基于线程
auto fut = std::async(doAsyncWork); // 基于任务
class ThreadRAII{ public: enum class DctorAction {join, detach}; ThreadRAII(std::thread&& t, DtorAction a) : action(a),t(std::move(t)) {} ~ThreadRAII() { if(t.joinable()){ if(action == DtorAction::join){ t.join(); }else{ t.detach(); } } } std::thread& get(){return t;} private: DtorAction action; std::thread t; } //使用示例: ThreadRAII t( std::thread([]{ //bla bla }), ThreadRAII::DtorAction::join );
void task(std::promise& prom) { int x = 0; /* 这里一般一个非常耗时耗cpu的操作如查找,计算等,在此过程中得到x的最终值,这里我们直接赋值为10 */ x = 10; prom.set_value(10); //设置共享状态的值,同时promise会设置为ready } void print_int(std::future& fut) { int x = fut.get(); //如果共享状态没有设置ready,调用get会阻塞当前线程 std::cout << "value: " << x << '\n'; } int main () { std::promise prom; // 生成一个 std::promise 对象. std::future fut = prom.get_future(); // 和 future 关联. std::thread th1(task, std::ref(prom)); // 将 prom交给另外一个线程t1 注:std::ref,promise对象禁止拷贝构造,以引用方式传递 std::thread th2(print_int, std::ref(fut)); // 将 future 交给另外一个线程t. /*主线程这里我们可以继续做一大堆我们想做的事,不用等待耗时的task线程,也不会因为等待task的执行结果而阻塞*/ th1.join(); th2.join(); return 0;
*
是无类型指针,就是可以指向其它任何类型的指针。NULL并不是指针空值,NULL可能被定义为字面常量或者是无类型(void*
)指针常量.char *ptr = NULL;if(ptr == nullptr)结果为true
) 0不可以与nullptr比较是否相等(部分老旧编译器可以)enum class Color{ balck, white, red };
Color c = white; //错误!,范围内并无名为“white”的枚举量
Color c = Color::white // 正确
using UPtrMapSS = std::unqiue_ptr<std::unordered_map<std::string,std::string>>
; 而非typedef std::unqiue_ptr<std::unordered_map<std::string,std::string>> UPtrMapSS
constexpr int pow(int base, int exp) noexcept { auto result = i; for(int i=0;i<exp; ++i) result *= base; return result; } // 则pow(x,y) 返回值是一个常量,只要x和y是常量或constexpr class Point{ public: constexpr Point(double x=0, double y=0) noexcept :x(xVal),y(yVal) {} constexpr double xValue() const noexcept { return x; } void setX(double newX) noexcept { x = newX; } constexpr Point p1(9.4,27.7); }
// for_each example #include <iostream> // std::cout #include <algorithm> // std::for_each #include <vector> // std::vector void myfunction (int i) { // function: std::cout << ' ' << i; } struct myclass { // function object type: void operator() (int i) {std::cout << ' ' << i;} } myobject; int main () { std::vector<int> myvector; myvector.push_back(10); myvector.push_back(20); myvector.push_back(30); std::cout << "myvector contains:"; for_each (myvector.begin(), myvector.end(), myfunction); std::cout << '\n'; // or: std::cout << "myvector contains:"; for_each (myvector.begin(), myvector.end(), myobject); std::cout << '\n'; return 0; }
priority_queue<int, vector<int>, greater<int> > p;
#include<iostream> #include<queue> #include<cstdlib> using namespace std; struct Node{ int x,y; Node(int a=0, int b=0):x(a), y(b) {} }; struct cmp{ bool operator()(Node a, Node b){ if(a.x == b.x) return a.y > b.y; return a.x > b.x; } }; int main(){ priority_queue<Node, vector<Node>, cmp>p; for(int i=0; i<10; ++i) p.push(Node(rand(), rand())); while(!p.empty()){ cout<<p.top().x<<' '<<p.top().y<<endl; p.pop(); } return 0; }
std::list< int> List;
std::list< int>::iterator itList;
for( itList = List.begin(); itList != List.end(); )
{
if( WillDelete( *itList) )
List.erase( itList++); //不可以删除后再++, 因为erase后,当前迭代器失效了。
else
itList++;
}
for (iter = cont.begin(); iter != cont.end();)
{
if (shouldDelete(*iter))
iter = cont.erase(iter);
else
++iter;
}
if(delete(iter)) iter = erase(iter); else iter++
的方式来迭代处理,其它的如map等通过上面的例子处理,如果是C++11则都用iter = erase(iter)
来处理std::pair <std::string, double> p, p.first p.second
,可以将pair作为一个类似于int的类型,如vector<pair<string, string>> tickets
#include<iostream> #include<vector> #include<string> #include<algorithm> using namespace std; typedef struct Fruits{ string name; int id; int weight; }Fruits; bool Compare(Fruits&a,Fruits&b){ //if(a.name!=b.name){ // return a.name<b.name; //} if(a.weight!=b.weight){ return a.weight<b.weight; } else{ return a.id<b.id; } } int main(){ int N; while(cin>>N){ vector<Fruits> ft; ft.clear(); for(int i=0;i<N;i++){ string tmname=""; int iid,wweight; cin>>tmname>>iid>>wweight; Fruits fft; fft.name=tmname; fft.id=iid; fft.weight=wweight; ft.push_back(fft); } sort(ft.begin(),ft.end(),Compare); // 从小到大排序 for(auto it=ft.begin();it!=ft.end();it++){ cout<<it->name<<' '<<it->id<<' '<<it->weight<<endl; } } return 0; }
vector<int>::reverse_iterator r = s.rbegin(); for(r; r != s.end() ; r++)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。