小奥的学习笔记

  • Home
  • Learning & Working
    • Speech Enhancement Notes
    • Programming language
    • Computer & DL
    • MOOC
  • Life
    • Life Time
    • Thinking & Comprehension
    • Volunteer
    • Plan
    • Travel
  • Footprints
  • GuestBook
  • About
    • About Me
    • 个人履历
    • 隐私策略
  1. 首页
  2. Study-notes
  3. Programming language
  4. C/C++
  5. 正文

C++面向对象程序设计课程笔记(第十周)

2018年9月23日 1937点热度 0人点赞 0条评论

第十周 C++11新特性和C++高级主题

第一节 C++新特性(1)

1.统一的初始化方法

(1)int arr[3]{1, 2, 3};

(2)vector<int> iv{1, 2, 3};

(3)map<int, string> mp{{1,"a"},{2,"b"}};

(4)string str{"Hello World"};

(5)int * p = new int[20]{1,2,3};

(6)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}; }

2.成员变量默认初始值

class B {

public:

int m = 1234;

int n;

};

int main(){

B b;

cout<<b.m<<endl;

return 0;

}

3.auto关键字

用于定义变量,编译器可以自动判断变量的类型。

auto i =100;//i是int

auto p = new A();//p是*A

 

map<string,int,greater<string> > mp;

for(auto i =mp.begin(); i!=mp.end(); ++i)

cout<<i->first<<","<<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类型

4.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&

5.智能指针share_ptr

头文件: <memory>。通过shared_ptr的构造函数,可以让shared_ptr对象托管一个new运算符返回的指针,写法如下:

shared_ptr<T> ptr(new T);  // T 可以是 int ,char, 类名等各种类型

此后ptr就可以像 T* 类型的指针一样来使用,即 *ptr 就是用new动态分配的那个对象,而且不必操心释放内存的事。

多个shared_ptr对象可以同时托管一个指针,系统会维护一个托管计数。当无shared_ptr托管该指针时,delete该指针。

shared_ptr对象不能托管指向动态分配的数组的指针,否则程序运行会出错。

例程1:

#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)

share_ptr<A> sp2(sp1);//sp2也托管A(2)

cout<<"1)"<<sp1->n<<","<<sp2->n<<endl;

//output: 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;

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 destruct11or

例程2:

#include <memory>

#include <iostream>

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);//并不增加 ptr中对p的托管计数

cout << "end" << endl;

return 0;

}

在ptr中托管了p,但是我在ptr2中也托管p的时候,并不增加ptr中对p的托管计数。因为ptr和ptr2认为他们所托管的p不是一个p(虽然实际上是一个p)。在输出end之后主程序结束,ptr和ptr2都要进行执行析构函数,p被执行了两次析构函数,系统会崩溃。所以输出结果如下:

end

~A

~A

(之后程序崩溃)

6.空指针nullptr

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,p3和p2类型不同

if(p3 == NULL)

cout<<"equal4"<<endl;

bool b = nullptr;//b=false

int i = nullptr;//error,nullptr无法自动转换成整型

return 0;

}

7.基于范围的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> str(ary,ary+5);

for(auto &it:st)

it.n*=10;

for(A it: st)

cout<<it.n<<",";

return 0;

}

类似于Java中的使用。

8.右值引用和move语义

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

class A { };

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

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

主要目的是提高程序运行的效率,减少需要进行深拷贝的对象进行深拷贝的次数。我们前面学习的都是左值引用!

第二节 C++新特性(2)

1.可移动但不可复制的对象

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;

}

2.无序容器(哈希表)

#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;

}

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

3.正则表达式

#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, 失败

return 0;

}

4.Lambda表达式

形式:

[外部变量访问方式说明符](参数表) ->返回值类型{

语句组

}

[]:不使用任何外部变量;

[=]:以传值的形式使用所有外部变量;

[&]:以引用形式使用所有外部变量;

[x, &y]:x以传值形式使用,y以引用形式使用;

[=,&x,&y]:x和y以引用形式使用,其余变量以传值形式使用;

[&,x,y]:x和y以传值的形式使用,其余变量以引用形式使用。

如果以传值方式进行传递对象,就不能修改该对象的值。“->返回值类型”也可以没有, 没有则编译器自动判断返回值类型。

例程:

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;

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<<" ";});//输出11 2 33 4

return 0;

}    要注意,[ ](double a,double b) { return a + b; }(1.2,2.5)中的(1.2,2.5)并不是lambda表达式一部分,而只是调用lambda表达式函数的赋值语句。

例程:实现递归求菲波那切数列第n项:

function<int(int)> fib = [&fib](int n)

{return n<=2?1:fib(n-1)+fib(n-2);};

function<int(int)>表示返回值为int,有一个int参数的函数。

第三节 强制类型转换

共有四种类型:static_cast、reinterpret_cast、const_cast和dynamic_cast。

1.static_cast

static_cast用来进用行比较“自然”和低风险的转换,比如整型和实数型、字符型之间互相转换。

static_cast不能来在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,也不能用于不同类型的引用之间的转换。

 

2.reinterpret_cast

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

例程:

#include <iostream>

using namespace std;

class A

{

public:

int i ,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,因为r是4个字节,所以上面的引用只引用了a的前面4个字节,也就是a.i

cout<<a.i<<","a.j<<endl;

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);

//两个不同类型的函数指针之间可以互相转换

return 0;

}

输出结果:200,100 400 5678abcd

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不能用于将非多态基类的指针或引用,强制转换为派生类的指针或引用。

例程如下:

#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;  //会执行

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

if( pd ==NULL)

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

return 0;

}

如下所示:Derived & r = dynamic_cast<Derived&>(b); 那该如何判断该转换是否安全呢?

答案:不安全则抛出异常。

第四节 异常处理

程序运行中总难免发生错误,我们希望不只是简单地终止程序运行,还能够反馈异常情况的信息:哪一段代码发生的、什么异常,还能够对程序运行中已发生的事情做些处理:取消对输入文件的改动、释放已经申请的系统资源。

1.异常处理

一个函数运行期间可能产生异常。在函数内部对异常进行处理未必合适。因为函数设计者无法知道函数调用者希望如何处理异常。我们需要告知函数调用者发生了异常,让函数调用者处理比较好,但是用函数返回值告知异常不方便。

(1)用try,catch进行异常处理

例程:

#include <iostream>

using namespace std;

int main()

{

double m ,n;

cin >> m >> n;

try {

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

if( n == 0)

throw -1; //抛出int类型异常

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;

}

表4.1 trycatch例程输入输出结果

程序输入 9 0 0 6
输出结果 before dividing

catch(...)

finished

 

before dividing

catch(double) -1

finished

 

 

注意:try块中定义的局部对象,发生异常时会析构!

(2)异常的再抛出

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

例程:

#include <iostream>

#include <string>

using namespace std;

class CException

{

public :

string msg;

CException(string s):msg(s) { }

};

double Devide(double x, double y){

if(y == 0)

throw CException("devided by zero");//抛出异常

cout << "in Devide" << endl;

return x / y;

}

int CountTax(int salary){//异常自己处理掉了

try{

if( salary < 0 )

throw -1;

cout << "counting tax" << endl;}

catch (int ) {

cout << "salary < 0" << endl;

}

cout << "tax counted" << endl;

return salary * 0.15;

}

int main(){

double f = 1.2;

try {

CountTax(-1);//在这个函数自己处理完了,try里面就感知不到这个错误了

f = Devide(3,0);//Devide本身没有处理异常,所以抛给了这个try里面了

cout << "end of try block" << endl;

}

catch(CException e) {

cout << e.msg << endl; }

cout << "f=" << f << endl;

cout << "finished" << endl;

return 0;

}

2.C++标准异常类

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

图4.1 exception派生出的异常类

(1)bad_cast

在用 dynamic_cast进行从多态基类对象(或引用),到派生类的引用的强制类型转换时,如果转换是不安全的,则会抛出此异常。

(2)bad_alloc

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

(3)out_of_range

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

 

3.运行时类型检查

C++运算符typeid是单目运算符,可以在程序运行过程中获取一个表达式的值的类型。typeid运算的返回值是一个type_info类的对象,里面包含了类型的信息。

例程如下:

#include <iostream>

#include <typeinfo> //要使用typeinfo,需要此头文件

using namespace std;

struct Base { };    //非多态基类

struct Derived : Base { };

struct Poly_Base {virtual void Func(){ } }; //多态基类struct Poly_Derived: Poly_Base { };

int main()

{

//基本类型

long i;  int * p = NULL;

cout << "1) int is: " << typeid(int).name() << endl;

//输出 1) int is: int

cout << "2) i is: " << typeid(i).name() << endl;

//输出 2) i is: long

cout << "3) p is: " <<  typeid(p).name() << endl;

//输出 3) p is: int *

cout << "4) *p is: " <<  typeid(*p).name() << endl ;

//输出 4) *p is: int

//非多态类型

Derived derived;

Base* pbase = &derived;

cout << "5) derived is: " << typeid(derived).name() << endl;

//输出 5) derived is: struct Derived

cout << "6) *pbase is: " << typeid(*pbase).name() << endl;

//输出 6) *pbase is: struct Base

cout << "7) " << (typeid(derived)==typeid(*pbase) ) << endl;

//输出 7) 0

//多态类型

Poly_Derived polyderived;

Poly_Base* ppolybase = &polyderived;

cout << "8) polyderived is: " << typeid(polyderived).name() << endl;

//输出 8) polyderived is: struct Poly_Derived

cout << "9) *ppolybase is: " << typeid(*ppolybase).name() << endl;

//输出 9) *ppolybase is: struct Poly_Derived

cout << "10) " << (typeid(polyderived)!=typeid(*ppolybase) ) << endl;

//输出 10) 0

return 0;

}

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: C++ C++学习笔记 C++慕课
最后更新:2018年9月23日

davidcheung

这个人很懒,什么都没留下

打赏 点赞
< 上一篇
下一篇 >

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

搜索
欢迎关注我的个人公众号
最新 热点 随机
最新 热点 随机
DEEPFILTERNET:一种基于深度滤波的全频带音频低复杂度语音增强框架 奥地利匈牙利九日游旅程 论文阅读之Study of the General Kalman Filter for Echo Cancellation 小奥看房之鸿荣源珈誉府 杭州往返旅途及西溪喜来登和万怡的体验报告 2022年的第一篇碎碎念
奥地利匈牙利九日游旅程小奥看房之鸿荣源珈誉府论文阅读之Study of the General Kalman Filter for Echo CancellationDEEPFILTERNET:一种基于深度滤波的全频带音频低复杂度语音增强框架
《鸟哥的Linux私房菜》(基础篇)笔记整理(第7章)Part.2 换个风格,换个心情 课上阅读材料有感 [leetcode]unique binary search trees 沉重悼念舟曲特大山洪泥石流中不幸遇难的同胞 奥地利匈牙利九日游旅程
标签聚合
python学习 高中 Java 算法 生活 leetcode Python 学习 linux 鸟哥的linux私房菜
最近评论
davidcheung 发布于 5 个月前(02月09日) The problem has been fixed. May I ask if you can s...
tk88 发布于 5 个月前(02月07日) Hmm is anyone else having problems with the pictur...
cuicui 发布于 8 个月前(10月20日) :wink:
niming 发布于 10 个月前(09月19日) 同级校友,能刷到太巧了
davidcheung 发布于 2 年前(08月16日) 我得找一下我之前整理的word文档看一下,如果找到了我就更新一下这篇文章。
Nolan 发布于 2 年前(07月25日) 您的笔记非常有帮助。贴图不显示了,可以更新一下吗?
davidcheung 发布于 3 年前(06月19日) 到没有看webrtc的代码。现在主要在看我们公司的代码了。。。只是偶尔看一看webrtc的东西。。。
aobai 发布于 3 年前(03月13日) gain_change_hangover_ 应该是每三个block 只能够调整一次,这样保证每帧...
匿名 发布于 5 年前(12月30日) 烫
小奥 发布于 5 年前(12月12日) webRTC里面的NS本身我记得就是在C++里面呀

COPYRIGHT © 2025 小奥的学习笔记. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

陕ICP备19003234号-1

鲁公网安备37120202000100号