第十週 C++11新特性和C++高級主題
1.C++11新特性(一)
2.C++11新特性(二)
3.強制類型轉換
4.異常處理
1.C++11新特性(一)
C++2011年的新的標準,gcc4.8編譯器完全支持C++11。
統一的初始化方法
int arr[3]{1, 2, 3};
vector<int> iv{1, 2, 3};
map<int, string> mp{{1, "a"}, {2, "b"}};
string str{"Hello World"};
int * p = new int[20]{1,2,3};
struct A {
int i,j;
A(int m,int n):i(m),j(n) { }
};
A func(int m,int n ) { return {m,n}; }
int main() { A * pa = new A {3,7}; }
成員變量默認初始值
class B
{
public:
int m = 1234;
int n;
};
int main()
{
B b;
cout << b.m << endl; //輸出 1234
return 0;
}
auto關鍵字
用於定義變量,編譯起可以自動判斷變量的類型,用auto的時候意味着必須初始化。
auto i = 100; // i 是 int
auto p = new A(); // p 是 A *
auto k = 34343LL; // k 是 long long
map<string,int,greater<string> > mp;
for( auto i = mp.begin(); i != mp.end(); ++i)
cout << i->first << "," << i->second ;
//i的類型是: map<string,int,greater<string> >::iterator
class A { };
A operator + ( int n,const A & a)
{
return a;
}
template <class T1, class T2>
auto add(T1 x, T2 y) -> decltype(x + y) {//-> decltype(x + y) 表明這個函數的額返回值類型是x+y決定的
return x+y;
}
auto d = add(100,1.5); // d是double d=101.5
auto d = add(100,A()); // d是A類型
decltype(表達式) 關鍵字
返回表達式的類型
int i;
double t;
struct A { double x; };
const A* a = new A();
decltype(a) x1; // x1 is A *
decltype(i) x2; // x2 is int
decltype(a->x) x3; // x3 is double
decltype((a->x)) x4 = t; // x4 is double&引用
智能指針shared_ptr,還是很有用的
頭文件:
是個類模板,通過shared_ptr的構造函數,可以讓shared_ptr對象託管一個new運算符返回的指針,寫法如下:
shared_ptr<T> ptr(new T); // T 可以是 int ,char, 類名等各種類型
此後ptr就可以像 T* 類型的指針一樣來使用,即 *ptr 就是用new動態分配的那個對象,而且不必操心釋放內存的事。
new出來的東西,如果所有託管它的智能指針都放棄託管它了,他就會自動被釋放,如果new的是個對象,那它就執行析構函數。
多個shared_ptr對象可以同時託管一個指針,系統會維護一個託管計數。當無shared_ptr託管該指針時,delete該指針。
sp1.get()成員函數可以去除取出智能指針託管的指針。A * p = sp1.get(); //p 指向 sp1託管的指針
sp1.reset()成員函數使智能指針放棄它託管的指針。
sp1.reset(q)成員函數使智能指針開始託管指針q。
shared_ptr對象不能託管指向動態分配的數組的指針,否則程序運行會出錯
#include <memory>
#include <iostream>
using namespace std;
struct A {
int n;
A(int v = 0):n(v){ }
~A() { cout << n << " destructor" << endl; }
};
int main()
{
shared_ptr<A> sp1(new A(2)); //sp1託管A(2)
shared_ptr<A> sp2(sp1); //sp2也託管 A(2),調用了複製構造函數
cout << "1)" << sp1->n << "," << sp2->n << endl;
//輸出1)2,2
shared_ptr<A> sp3;
A * p = sp1.get(); //p 指向 A(2)
cout << "2)" << p->n << endl;
sp3 = sp1; //sp3也託管 A(2)
cout << "3)" << (*sp3).n << endl; //輸出 2
sp1.reset(); //sp1放棄託管 A(2)
if( !sp1 )//sp1沒有託管的指針
cout << "4)sp1 is null" << endl; //會輸出
A * q = new A(3);
sp1.reset(q); // sp1託管q
cout << "5)" << sp1->n << endl; //輸出 3
shared_ptr<A> sp4(sp1); //sp4託管A(3)
shared_ptr<A> sp5;
//sp5.reset(q);//不妥,導致程序出錯,爲什麼?
sp1.reset(); //sp1放棄託管 A(3),託管它的智能指針都放棄託管,他就會自動被釋放,析構函數
cout << "before end main" <<endl;
sp4.reset(); //sp1放棄託管 A(3),但是還有智能指針託管它,所以析構函數不會被調用
cout << "end main" << endl;
return 0; //程序結束,會delete 掉A(2),析構函數被調用
}
輸出結果:
1)2,2
2)2
3)2
4)sp1 is
null
5)3
before end
main
3
destructor
#include <iostream>
#include <memory>
using namespace std;
struct A{
~A() { cout << "~A" << endl; }
};
int main()
{
A * p = new A();
shared_ptr<A> ptr(p);
shared_ptr<A> ptr2;
ptr2.reset(p); //雖然這裏也是p,但編譯器不會讓ptr2也託管p,並不增加 ptr中對p的託管計數,爲啥?
cout << "end" << endl;
return 0;
}
輸出:
end
~A
~A
之後程序崩潰,因爲p被delete兩次
空指針nullptr,類似NULL
#include <memory>
#include <iostream>
using namespace std;
int main() {
int* p1 = NULL;
int* p2 = nullptr;
shared_ptr<double> p3 = nullptr;
if(p1 == p2)
cout << "equal 1" <<endl;
if( p3 == nullptr)
cout << "equal 2" <<endl;
//if( p3 == p2) ; // error,編譯出錯
if( p3 == NULL)
cout << "equal 4" <<endl;
bool b = nullptr; // b = false,nullptr可以被自動轉換成布爾值false
int i = nullptr; //error,nullptr不能自動轉換成整型
return 0;
}
輸出:
equal 1
equal 2
equal 4
基於範圍的for循環
牛逼啊,高大上!!!!裝逼神器
#include <iostream>
#include <vector>
using namespace std;
struct A {
int n;
A(int i):n(i) { }
};
int main() {
int ary[] = {1,2,3,4,5};
for(int & e: ary)
e*= 10;
for(int e : ary)
cout << e << ",";
cout << endl;
vector<A> st(ary,ary+5);
for( auto & it: st)
it.n *= 10;
for( A it: st)
cout << it.n << ",";
return 0;
}
輸出:
10,20,30,40,50,
100,200,300,400,500,
右值引用和move語義,很有趣
右值:一般來說,不能取地址的表達式,就是右值,能取地址的,就是左值
class A { };
A & r = A(); // error , A()是無名變量,是右值,不能取臨時變量的地址
A && r = A(); //ok, r 是右值引用
普通的&是左值引用,&&是右值引用,可以引用右值,如臨時變量。
主要目的是提高程序運行的效率,減少需要進行深拷貝的對象進行深拷貝的次數。有意識地避免深拷貝。
例子:自己寫的string類
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class String
{
public:
char * str;
String():str(new char[1]) { str[0] = 0;}//無參構造函數
String(const char * s) {//構造函數
str = new char[strlen(s)+1];
strcpy(str,s);
}
String(const String & s) {//複製構造函數
cout << "copy constructor called" << endl;
str = new char[strlen(s.str)+1];
strcpy(str,s.str);
}
String & operator=(const String & s) {//重載賦值號,深拷貝
cout << "copy operator= called" << endl;
if( str != s.str) {
delete [] str;
str = new char[strlen(s.str)+1];
strcpy(str,s.str);
}
return * this;
}
//移動構造函數
String(String && s):str(s.str) {//移動構造函數,右值引用
cout << "move constructor called"<<endl;
s.str = new char[1];//讓原str指向一塊新的空白,鳩佔鵲巢,就節省了深拷貝的時間
s.str[0] = 0;
}
//移動賦值號,和移動構造函數類似
String & operator = (String &&s) {
cout << "move operator= called"<<endl;
if (str!= s.str) {
delete [] str;
str = s.str;
s.str = new char[1];
s.str[0] = 0;
}
return *this;
}
~String() { delete [] str; }//析構函數
};
template <class T>
void MoveSwap(T& a, T& b) {//前提是調用完這個函數後a,b不會再用了,因爲出了這個函數a,b就被指向新的空內存了
T tmp(move(a)); // 將左值a變爲右值,std::move(a)爲右值,這裏會調用移動構造函數
a = move(b); // move(b)爲右值,因此這裏會調用移動賦值號
b = move(tmp); // move(tmp)爲右值,因此這裏會調用移動賦值號
}
int main()
{
//String & r = String("this"); // error
String s;
s = String("ok"); // String("ok")是右值,調用移動賦值號
cout << "******" << endl;
String && r = String("this");
cout << r.str << endl;
String s1 = "hello",s2 = "world";
MoveSwap(s1,s2);
cout << s2.str << endl;
return 0;
}
輸出:
move operator= called
******
this
move constructor called
move operator= called
move operator= called
hello
move()表達式:轉換成右值
函數返回值爲對象時,返回值對象如何初始化?
寫複製構造函數:
return 局部對象 -> 複製
return 全局對象 ->複製
寫移動構造函數:
return 局部對象 -> 移動
return 全局對象 ->默認複製
return move(全局對向) -〉移動
同時寫 複製構造函數和 移動構造函數:
return 局部對象 -> 移動
return 全局對象 -> 複製
return move(全局對向) -〉移動
dev c++中,return 局部對象 會導致優化,不調用移動或複製構造函數
可移動但不可複製的對象:
struct A{
A(const A & a) = delete;
A(const A && a) { cout << "move" << endl; }
A() { };
};
A b;
A func() {
A a;
return a;
}
void func2(A a) { }
int main() {
A a1;
A a2(a1); //compile error
func2(a1); //compile error
func();
return 0;
}