【北京大学】程序设计实习 (C++版)第10周 C++11

目录

第10周 C++11

10-1  C++11新特性     

10-2  C++11新特性(续)    

10-3  强制类型转化

10-4  异常处理


第10周 C++11

10-1  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 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) {

            return x+y;

}

auto d = add(100,1.5); // d是double d=101.5

auto k = 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
  1. 头文件: <memory>
  2. 通过shared_ptr的构造函数,可以让shared_ptr对象托管一个new运算符返回的指针,写法如下:shared_ptr<T> ptr(new T); // T 可以是 int ,char, 类名等各种类型
  3. 此后ptr就可以像 T* 类型的指针一样来使用,即 *ptr 就是用new动态分配的那个对象,而且不必操心释放内存的事。
  4. 多个shared_ptr对象可以同时托管一个指针,系统会维护一个托管计数。当无shared_ptr托管该指针时,delete该指针。

 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)

        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)

}

运行结果:

 

  • 空指针nullptr

#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

   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;

}

运行结果:

 

右值引用和move语义

右值:一般来说,不能取地址的表达式,就是右值,

能取地址的,就是左值

class A { };

A & r = A(); // error , A()是无名变量,是右值

A && r = A(); //ok, r 是右值引用

主要目的是提高程序运行的效率,减少需要进行深拷贝的对象进行深拷贝的次数。

 

10-2  C++11新特性(续)    

  • 无序容器(哈希表)

#include <iostream>

#include <string>

#include <unordered_map>

using namespace std;

int main()

{

         unordered_map<string,int> turingWinner; //图灵奖获奖名单

         turingWinner.insert(make_pair("Dijkstra",1972));

         turingWinner.insert(make_pair("Scott",1976));

         turingWinner.insert(make_pair("Wilkes",1967));

         turingWinner.insert(make_pair("Hamming",1968));

         turingWinner["Ritchie"] = 1983;

         string name;

         cin >> name; //输入姓名

         unordered_map<string,int>::iterator p = turingWinner.find(name);

         //据姓名查获奖时间

         if( p != turingWinner.end())

         cout << p->second;

         else

         cout << "Not Found" << endl;

         return 0;

}

哈希表插入和查询的时间复杂度几乎是常数

 

  • 正则表达式

#include <iostream>

#include <regex> //使用正则表达式须包含此文件

using namespace std;

int main()

{

         regex reg("b.?p.*k");

         cout << regex_match("bopggk",reg) <<endl; //输出 1, 表示匹配成功

         cout << regex_match("boopgggk",reg) <<endl; //输出 0, 表示匹配失败

         cout << regex_match("b pk",reg) <<endl; //输出 1, 表示匹配成功

         regex reg2("\\d{3}([a-zA-Z]+).(\\d{2}|N/A)\\s\\1");

         string correct="123Hello N/A Hello";

         string incorrect="123Hello 12 hello";

         cout << regex_match(correct,reg2) <<endl; //输出 1, 表示匹配成功

         cout << regex_match(incorrect,reg2) << endl; //输出 0, 表示匹配失败

}

 

  • Lambda表达式

只使用一次的函数对象,能否不要专门为其编写一个类?

只调用一次的简单函数,能否在调用时才写出其函数体?

 

程序示例:

int a[4] = { 4,2,11,33};
sort(a,a+4,[ ](int x,int y)->bool { return x%10 < y%10; });
for_each(a,a+4,[ ](int x) {cout << x << " " ;} ) ;

程序示例1

#include <iostream>

using namespace std;

 

int main() {

    int x = 100, y = 200, z = 300;

    cout << [ ](double a, double b){return a + b; }(1.2, 2.5)<< endl;

    auto ff = [=, &y, &z](int n) {

        cout << x << endl;

        y++, z++;

        return n * n;

    };

 

    cout << ff(15) << endl;

    cout << y << "," << z << endl;

    return 0;

}

运行结果:

 

程序示例2

#include <iostream>

#include <algorithm>

#include <vector>

using namespace std;

int main()

{

    vector<int> a{1,2,3,4};

    int total = 0;

    for_each(a.begin(), a.end(), [&](int&x) {total += x; x *= 2; });

    cout << total << endl;

 

    for_each(a.begin(), a.end(), [](int&x) { cout << x << endl; });

    return 0;

}

运行结果:

 

10-3  强制类型转化

强制类型转换

static_cast、 interpret_cast、const_cast和dynamic_cast

1. static_cast

static_cast用来进用行比较“自然”和低风险的转换,比如整型和实数型、字符型之间互相转换。static_cast不能来在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,也不能用于不同类型的引用之间的转换。

程序示例1:

//static_cast示例

#include <iostream>

using namespace std;

class A

{

public:

         operator int() { return 1; }

         operator char * (){ return NULL; }

};

int main()

{

         A a;

         int n; char * p = "New Dragon Inn";

         n = static_cast<int>(3.14); // n 的值变为 3

         n = static_cast<int>(a); //调用a.operator int, n的值变为

         p = static_cast<char*>(a); //调用a.operator char *,p的值变为 NULL

         n = static_cast<int> (p); //编译错误, static_cast不能将指针转换成整型

         p = static_cast<char*>(n); //编译错误, static_cast不能将整型转换成指针

         return 0;

}

 

2. reinterpret_cast

reinterpret_cast用来进行各种不同类型的指针之间的转换、不同类型的引用之间转换、以及指针和能容纳得下指针的整数类型之间的转换。转换的时候,执行的是逐个比特拷贝的操作。

//reinterpret_cast示例

#include <iostream>

using namespace std;

class A

{

public:

int i;

int j;

A(int n):i(n),j(n) { }

};

int main()

{

         A a(100);

         int & r = reinterpret_cast<int&>(a); //强行让 r 引用 a

         r = 200; //把 a.i 变成了 200

         cout << a.i << "," << a.j << endl; // 输出 200,100

         int n = 300;

         A * pa = reinterpret_cast<A*> ( & n); //强行让 pa 指向 n

         pa->i = 400; // n 变成 400

         pa->j = 500; //此条语句不安全,很可能导致程序崩溃

         cout << n << endl; // 输出 400

         long long la = 0x12345678abcdLL;

         pa = reinterpret_cast<A*>(la); // la太长,只取低32位0x5678abcd拷贝给pa

         unsigned int u = reinterpret_cast<unsigned int>(pa); //pa逐个比特拷贝到u

         cout << hex << u << endl; //输出 5678abcd

         typedef void (* PF1) (int);

         typedef int (* PF2) (int,char *);

         PF1 pf1; PF2 pf2;

         pf2 = reinterpret_cast<PF2>(pf1); //两个不同类型的函数指针之间可以互相转换

}

 

3. const_cast

用来进行去除const属性的转换。将const引用转换成同类型的非const引用,将const指针转换为同类型的非const指针时用它。例如:

const string s = “Inception”;

string & p = const_cast<string&>(s);

string * ps = const_cast<string*>(&s); // &s的类型是const string *

 

4. dynamic_cast

dynamic_cast专门用于将多态基类的指针或引用,强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回NULL指针。 dynamic_cast不能用于将非多态基类的指针或引用,强制转换为派生类的指针或引用

 

//dynamic_cast示例

#include <iostream>

#include <string>

using namespace std;

class Base

{ //有虚函数,因此是多态基类

public:

         virtual ~Base() { }

};

class Derived:public Base { };

int main()

{

         Base b;

         Derived d;

         Derived * pd;

         pd = reinterpret_cast<Derived*> ( &b);

         if( pd == NULL) //此处pd不会为NULL。

                  //reinterpret_cast不检查安全性,总是进行转换

                  cout << "unsafe reinterpret_cast" << endl;

         //不会执行

         pd = dynamic_cast<Derived*> ( &b);

         if( pd == NULL)

        

         //结果会是NULL,因为 &b不是指向派生类对象,此转换不安全

         cout << "unsafe dynamic_cast1" << endl; //会执行

         Base * pb = & d;

         pd = dynamic_cast<Derived*> ( pb); //安全的转换

         if( pd == NULL) //此处pd 不会为NULL

         cout << "unsafe dynamic_cast2" << endl; //不会执行

         return 0;

}

输出结果:

unsafe dynamic_cast1

10-4  异常处理

 

 

 

程序示例1

//捕获任何异常的catch块

#include <iostream>

using namespace std;

int main()

{

    double m, n;

    cin >> m >> n;

    try {

        cout << "before dividing." << endl;

        if (n == 0)

            throw - 1; //抛出整型异常

        else if (m == 0)

            throw - 1.0; //抛出double型异常

        else

            cout << m / n << endl;

        cout << "after dividing." << endl;

    }

        catch (double d) {

        cout << "catch(double) " << d << endl;

    }

    catch (...) {

        cout << "catch(...) " << endl;

    }

    cout << "finished" << endl;

    return 0;

}

运行结果:

 

 

 

异常的再抛出

         如果一个函数在执行的过程中,抛出的异常在本函数内就被catch块捕获并处理了,那么该异常就不会抛给这个函数的调用者(也称“上一层的函数” );如果异常在本函数中没被处理,就会被抛给上一层的函数。

C++标准异常类

C++标准库中有一些类代表异常,这些类都是从exception类派生而来。常用的几个异常类如下:

 

程序示例1:

#include <iostream>
#include <stdexcept>
#include <typeinfo>
using namespace std;
class Base
{
·virtual void func(){}
};
class Derived : public Base
{
public:
·void Print() { }
};
void PrintObj( Base & b)
{
         try {
                  Derived & rd = dynamic_cast<Derived&>(b);
                  //此转换若不安全,会抛出bad_cast异常
                  rd.Print();
         }
         catch (bad_cast& e) {
                  cerr << e.what() << endl;
         }
}
int main ()
{
         Base b;
         PrintObj(b);
         return 0;
}
输出结果:
Bad dynamic

 

程序示例2:

bad_alloc

在用new运算符进行动态内存分配时,如果没有足够的内存,则会引发此异常。

#include <iostream>

#include <stdexcept>

using namespace std;

int main ()

{

         try {

                  char * p = new char[0x7fffffff]; //无法分配这么多空间,会抛出异常

         }

         catch (bad_alloc & e) {

                  cerr << e.what() << endl;

         }

         return 0;

}

输出结果:

bad allocation

 

程序示例3:

out_of_range

用vector或string的at成员函数根据下标访问元素时,如果下标越界,就会抛出此异常。例如:

#include <iostream>

#include <stdexcept>

#include <vector>

#include <string>

using namespace std;

int main ()

{

         vector<int> v(10);

         try {

         v.at(100)=100; //抛出out_of_range异常

         }

         catch (out_of_range& e) {

                   cerr << e.what() << endl;

         }

         string s = "hello";

         try {

                  char c = s.at(100); //抛出out_of_range异常

         }

         catch (out_of_range& e) {

                  cerr << e.what() << endl;

         }

         return 0;

}

输出结果:

invalid vector<T> subscript

invalid string position

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值