iv{1, 2, 3};模板类map mp{{1, "a"}, {2, "b"}};以堆开辟数组int * p = new int[20]{1,2,3};成员变量默认初始值普通变量..._shared ptr无法托管数组">
当前位置:   article > 正文

c++11特性_shared ptr无法托管数组

shared ptr无法托管数组
2020.02.10
  • 1

声明:学习资源来源于中国MOOC,此处仅供总结学习。


一致初始化方法

  1. 整型数组int arr[3]{1, 2, 3};
  2. 字符数组string str{"Hello World"};
  3. 容器vector<int> iv{1, 2, 3};
  4. 模板类map<int, string> mp{{1, "a"}, {2, "b"}};
  5. 以堆开辟数组int * p = new int[20]{1,2,3};

成员变量默认初始值

普通变量可以在类里面直接赋值

class A{
public:
	int num = 10;//定义时直接赋值
	int n ;
}
int main()
{
	A a;
	cout<< a.num <<endl;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

auto关键字

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

  1. auto 变量必须在定义时初始化,这类似于const关键字。
auto num1 = 100;//整型,相当于int
auto p = new A();//类指针型;
auto num2 = 5201314LL;//长整型
map<string,int ,less<string> >mp;
for(auto i = 0;i != mp.end();i++)
	cout<< i->first <<" "<< i->second << endl;
//i的类型是map<string,int,less<string> >::iterator;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. 当我们希望从表达式的值推断出要定义的变量的类型,但不想用该表达式的值初始化变量时,可以使用decltype。
    在这里插入图片描述
    注意使用表达式一定要在auto的基础上加decltype,不然会出错,如图
    在这里插入图片描述
    为什么decltype放在函数后面呢?因为auto在这里的作用称为返回值占位,它只是为函数返回值占了一个位置,真正的返回值是后面的decltype( x + y);如果放置在前面,那么`decltype( x + y) add(T1 x,T2 y),其中使用声明的时候x与y还没有定义,会编译错误。
    在这里插入图片描述
  2. 定义在一个auto序列的变量必须始终推导成同一类型。
  3. 如果初始化表达式是引用,则去除引用功能。
    如例1:
    在这里插入图片描述
  4. 如果auto关键字带上&号,则不去除const语意
    例2:
    在这里插入图片描述
  5. 初始化表达式为数组时,auto关键字推导类型为指针;其中基于范围的for循环,可以直接定义for(auto 指代名称:容器名(数组))
    在这里插入图片描述

智能指针shared_ptr

  1. 头文件:#include<memory>
  2. 通过shared_ptr的构造函数,可以让shared_ptr对象托管一个new运算符返回的指针,写法如下:share_ptr<T>ptr(new T);此后ptr就可以像 T* 类型的指针一样来使用,即 *ptr 就是用new动态分配的那个对象,而且不必操心释放内存的事。
  3. 多个shared_ptr对象可以同时托管一个指针,系统会维护一个托管计数。当 无shared_ptr托管该指针时,delete该指针。
  4. shared_ptr对象不能托管指向动态分配的数组的指针,否则程序运行会出错。
    在这里插入图片描述

但是不能如此定义,如下图:
在这里插入图片描述

在这里插入图片描述
因为ptr1和ptr2都是托管p,但是由于同时定义时,他们不会共享一个托管指针的技术,会各自计数,都是1,那么会导致p的析构函数调用两次,严重会导致程序崩溃。


空指针nullptr

由于在c++中NULL = 0,此时指针为空,是整数类型,而nullptr是字符类型字面值,且nullptr不能自动转换成整型。

在这里插入图片描述


右值引用和move语义

所谓的右值引用,首先当右值的一般是不能取地址的参数,比如常数,临时对象等等,那么为了就为右值创建名称来引用即可。 而变量就可以取地址,这叫左值引用。

class A{ };
A &&T = A();//临时对象,右值引用;注意形式一般是“&&”
  • 1
  • 2

一般格式: 类型 && 引用名 = 右值表达式;
而右值引用的主要目的是提高程序运行的效率,减少需要进行深拷贝的对象进行深拷贝 的次数。

#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]; 
		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){
	T temp(move(a));// std::move(a)为右值,这里会调用move constructor
	a = move(b);//move(b)为右值,因此这里会调用move assigment
	b = move(temp);//move(temp)为右值,因此这里会调用move assigment
}
int main() 
{
	String s; 
	s = 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

在这里插入图片描述
规则·:

  • 左值引用, 使用 T&, 只能绑定左值
  • 右值引用, 使用 T&&, 只能绑定右值
  • 常量左值, 使用 const T&, 既可以绑定左值又可以绑定右值
  • 已命名的右值引用,编译器会认为是个左值

详细介绍,点击此处了解更多

编译器能自动生成类之间赋值、拷贝构造函数,在一定程度上减轻了代码量,但对于某些特定情况下,我们是不允许对象之间进行拷贝与赋值的,那么这时候,我们就需要考虑将拷贝、赋值构造函数禁用了,那么如下例使用:
在这里插入图片描述
C++11 标准引入了一个新特性:"=delete"函数。程序员只需在函数声明后加上“=delete;”并且需要注意的是必须在函数第一次声明的时候将其声明为 = delete 函数,否则编译器会报错。
在这里插入图片描述


无序容器(哈希表)

在这里插入图片描述
在这里插入图片描述
详情见无序关联容器(C++11)


正则表达式

正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。

  1. “ * ”前面的字符出现0次,1次,多次
  2. “ ? "前面的字符出现1次或0次
  3. " + "前面的字符出现至少一次

正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。
点击了解更多指令规则


Lambda表达式

形式:

[外部变量访问方式说明符] (参数表) ->返回值类型 {
语句组 
}(参数1值,参数2值,...
  • 1
  • 2
  • 3

定义规则:
[ ] 不使用任何外部变量 ;
[=] 以传值的形式使用所有外部变量;
[&] 以引用形式使用所有外部变量 ;
[x, &y] x 以传值形式使用,y 以引用形式使用;
[=,&x,&y] x,y 以引用形式使用,其余变量以传值形式使用;
[&,x,y] x,y 以传值的形式使用,其余变量以引用形式使用;

注意:“->返回值类型”也可以没有,没有则编译器自动判断返回值类型。

示例:

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
  • 2
  • 3

输出: 11 2 33 4
在这里插入图片描述
lambda表达式有如下优点:

1).声明式编程风格:就地匿名定义目标函数或函数对象,不需要额外写一个命名函数或者函数对象。以更直接的方式去写程序,好的可读性和可维护性。

2).简洁:不需要额外再写一个函数或者函数对象,避免了代码膨胀和功能分散,让开发者更加集中精力在手边的问题,同时也获取了更高的生产率。

3).在需要的时间和地点实现功能闭包,使程序更灵活。

示例1:
在这里插入图片描述
其中for_each()用于访问每一个元素成员。

示例2:
实现递归的斐波那契数列

#include<iostream>
#include<algorithm>
#include <functional>
using namespace std;
int main()
{
	function<int(int)> fib = [&fib](int n)
	{ return n <= 2 ? 1 : fib(n - 1) + fib(n - 2); };
	cout << fib(5) << endl;
	return 0;
}
//function<int(int)> 表示返回值为 int,并不是将返回值传给fib,而是<>中参数, 有一个int参数的函数
//而此处fib可以当作函数指针等运用。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

执行效果
在这里插入图片描述
详情请点击了解funtional库文件使用


类型强制转换

  1. static_cast
    static_cast用来进用行比较“自然”和低风险的转换,比 如整型和实数型、字符型之间互相转换。
    static_cast不能来在不同类型的指针之间互相转换,也不 能用于整型和指针之间的互相转换,也不能用于不同类型的 引用之间的转换。
    在这里插入图片描述
  2. reinterpret_cast
    reinterpret_cast用来进行各种不同类型的指针之间的转换、不同 类型的引用之间转换、以及指针和能容纳得下指针的整数类型之间的转换。转换的时候,执行的是逐个比特拷贝的操作。
    在这里插入图片描述
  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 *
  • 1
  • 2
  • 3
  1. dynamic_cast
  • dynamic_cast专门用于将多态基类的指针或引用,强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回NULL 指针。
  • dynamic_cast不能用于将非多态基类的指针或引用, 强制转换为派生类的指针或引用.

由于用派生类指针引用基类的对象。这种引用方式会导致语法错误。派生类指针必须先强制转换为基类指针,这种方法是安全的。

#include<iostream>
#include<string>
using namespace std;
class base {
public:
	virtual ~base(){ }
};
class based_on :public base { };
int main()
{
	base b;
	based_on d;
	based_on* p;
	p = reinterpret_cast<based_on*>(&b);//基类指针,转化成派生类,不检查错误
	if (p == NULL)
		cout << "unsafe reinterpret_cast" << endl;
	p = dynamic_cast<based_on*>(&b);//下行,基类指针,转化成派生类,检查错误
	if(p == NULL)
		cout << "unsafe dynamic_cast1" << endl;
	p = dynamic_cast<based_on*>(&d);//上行,派生类指针,检查错误
	if(p == NULL)
		cout << "unsafe dynamic_cast2" << endl;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

执行效果
在这里插入图片描述
满足不出错的情况一般是子类转化成父类指针或引用,子类与子类的转化等。


异常处理

经常写代码的同学肯定或多或少会遇到各种程序运行的异常,经常的原因是要么代码的质量不高,存在bug;要么输入的数据不符合设计的要求;要么算法设计考虑不周全等等。
那么如何正确处理发生的异常也是值得学习的。

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

那么在c++11中,try与catch两个函数常用来进行异常处理:

#include<iostream>
#include<string>
using namespace std;
int main()
{
	double m, n;
	cin >> m >> n;
	try {
		cout << "before dividing." << endl;
		if (n == 0)
			throw - 1;//抛出-1整型异常
		if (m == 0)
			throw -0.2;
		else
			cout << m / n << endl;
		cout << "after dividing" << endl;
	}
	catch (double d) {
		cout << "catch(double) " << d << endl;
	}
	catch (int e) {
		cout << "catch(int) " << e << endl;
	}
	cout << "finished" << endl; 
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

执行效果
在这里插入图片描述
在这里插入图片描述
只要抛出一个异常try内的程序就会结束进行,跳到catch函数处。

异常的再抛出
如果一个函数在执行的过程中,抛出的异常在本函数内就被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) {//本函数内就被catch块捕获并处理
		cout << "salary < 0" << endl;
	}
	cout << "tax counted" << endl; 
	return salary * 0.15;
}
int main() {
	double f = 1.2; 
	try {
		CountTax(-1);
		f = Devide(3, 0); //抛出的是临时对象,不会改变f的值,也没有重载
		cout << "end of try block" << endl;
	}
	catch (CException e) {//捕捉处理
		cout << e.msg << endl;
	}
	cout << "f=" << f << endl; 
	cout << "finished" << endl; 
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

执行效果
在这里插入图片描述
其中throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。

如果您想让 catch 块能够处理 try 块抛出的任何类型的异常,则必须在异常声明的括号内使用省略号 …,如下所示:

try{
   // 保护代码
}
catch(...){
  // 能处理任何异常的代码
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

C++标准异常类

C++标准库中有一些类代表异常,这些类都是从exception类派生而来。
在这里插入图片描述

  1. bad_cast 在用 dynamic_cast进行从多态基类对象(或引用),到派生类的引用的强制类型 转换时,如果转换是不安全的,则会抛出此异常。
    在这里插入图片描述
  2. bad_alloc 在用new运算符进行动态内存分配时,如果没有足够的内存,则会引发此异常。
  3. out_of_range 用vector或string的at成员函数根据下标访问元素时,如果下标越界,就会抛出 此异常。
    在这里插入图片描述
    详情,点击了解更多

运行时类型检查

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

#include<iostream>
#include<typeinfo>
using namespace std;
class A { };
class a:public A{ };
class B {
	virtual void show(){}
};
class b:public B{ };
int main()
{
//基本类型
	char c;
	char* p = nullptr;
	cout << "typeid(int).name  "<< typeid(int).name() << endl;
	cout << "typeid(char).name  " << typeid(char).name() << endl;
	cout << "typeid(p).name  " << typeid(p).name() << endl;
	cout << "typeid(*p).name  " << typeid(*p).name() << endl;
//非多态类型
	a t;
	A* aa = &t;
	cout << "typeid(a).name  " << typeid(a).name() << endl;
	cout << "typeid(*aa).name  " << typeid(*aa).name() << endl;
//多态类型
	b tt;
	B* bb = &tt;
	cout << "typeid(b).name  " << typeid(b).name() << endl;
	cout << "typeid(*bb).name  " << typeid(*bb).name() << endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

执行效果
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/664620
推荐阅读
相关标签