目录
异常处理的作用: 异常处理分离了接收和处理错误代码。为了替代error。
C++ 异常处理涉及到三个关键字:try、catch、throw。
1、throw: 当问题出现时,程序会抛出一个异常。通过使用 throw 关键字来完成。
2、try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。
3、catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
4、如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。
区别:如果是定义一个普通指针指向堆空间,则需要手动释放该指针才能调用析构函数,因为该指针的生命周期是delete。如果是智能指针,则不需要手动释放该指针也可以调用析构函数。
一、异常处理
异常处理的作用: 异常处理分离了接收和处理错误代码。为了替代error。
C++ 异常处理涉及到三个关键字:try、catch、throw。
1、throw: 当问题出现时,程序会抛出一个异常。通过使用 throw 关键字来完成。
格式:throw 错误代码。
2、try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。
格式:try { 测试代码 }
3、catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
格式:catch(错误代码类型 变量){ }
4、如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。
try //测试代码
{
//发现异常 throw 抛出异常
}catch( ExceptionName e1 ) //catch 捕抓异常
{
// catch 块
}
demo
//设计一个字符串类,当用户访问下标越界时,抛出异常
#include<iostream>
using namespace std;
int main()
{
try{
string s = "hello";
int i=0;
while(1){
if(s[i]=='\0'){
throw 101;
break;
}
i++;
}
}
catch(int e)
{
cout << "下标越界" << e << endl;
}
catch(...) //可以接收任何的数据类型
{
cout << "下标越界" << endl;
}
}
可以在任意的地方抛出异常,当前抛出的异常未被处理时,程序会死亡。
标准错误格式
try
{
测试代码
}
catch(const std::exception& e) //捕捉系统的所有异常。
{
std::cerr << e.what() << '\n';
}
错误原因
demo2
#include <iostream>
using namespace std;
int main()
{
//int *p = new int[1000000000000000000];
try{
//分配的空间过大,会抛出系统异常
int *p = new int[1000000000000000000];
}catch(const std::exception& e){ //处理系统异常
//输出异常的原因
std::cerr << e.what() << '\n';
}
cout << "程序继续运行" << endl;
}
二、c++11新特性
1、auto变量
作用:自动推导数据类型,方便使用。但在定义的时候要初始化。
auto a = 10086;
auto b = "hello world";
作用:自动推导变量的类型,一定要有数值才能推导。
auto c; //错误,没有数据值,无法推导
补充: decltype 分析变量的类型
int i = 1;
decltype(i) b = 2; // b是int decltype(i) 就是 int
2、枚举循环
例子:
#include <iostream>
using namespace std;
int main(int argc, char const *argv[])
{
int a[3] = {1,2,3};
枚举数组a中的各个元素,并赋值给 i
for(int i:a){
cout << i << endl;
}
return 0;
}
demo
#include<iostream>
#include<list>
using namespace std;
int main()
{
list<string> ls;
ls.push_back("小明");
ls.push_back("小红");
ls.push_back("小芳");
ls.push_back("小贝");
//枚举格式:for(数据类型 对象 : 容器对象) { }
ls.sort(); //排序函数
for(string vet:ls){ //枚举
cout << vet << endl;
}
cout << "--------翻转-----------\n";
ls.reverse(); //翻转函数
for(string vet:ls){
cout << vet << endl;
}
}
3、final & override
(1)final 用于修饰一个类,表示禁止该类进一步派生和虚函数的进一步重载,即禁止派生和虚函数重载。
final:类不能被继承
class Base final {
virtual void func() {
cout << "base" << endl;
}
};
class Derived : public Base{ // 编译失败,final修饰的类不可以被继承
void func() override {
cout << "derived" << endl;
}
};
(2)override 用于修饰派生类中的成员函数,标明该函数重写了基类函数,如果一个函数声明了override但父类却没有这个虚函数,编译报错,使用override关键字可以避免开发者在重写基类函数时无意产生的错误。即子类使用了父类没有的虚函数,则报错。
override 例子声明函数是重写函数
class Base {
virtual void func() {
cout << "base" << endl;
}
};
class Derived : public Base{
void func() override { // 确保func被重写
cout << "derived" << endl;
}
void fu() override { // error,基类没有fu(),不可以被重写
}
};
(3)explicit 禁止隐式转换
#include <iostream>
#include <list>
using namespace std;
class A {
A(int value) { // 没有explicit关键字,可以隐式转换
cout << "value" <<value << endl;
}
explicit A(string value) { // explicit关键字,不能隐式转换
cout << "value"<< value << endl;
}
};
int main() {
A a = 1; // 可以隐式转换,调用 A(int value)
string s="hello";
A b = s;
return 0;
}
4、随机数功能(比rand随机一点)
#include <time.h>
#include <iostream>
#include <random>
using namespace std;
int main() {
//产生随机因子
std::default_random_engine random(time(nullptr));
//设置随机范围
std::uniform_int_distribution<int> int_dis(0, 100); // 整数均匀分布
std::uniform_real_distribution<float> real_dis(0.0, 1.0); // 浮点数均匀分布
for (int i = 0; i < 10; ++i) {
//输出随机数
cout << int_dis(random) << ' ';
}
cout << endl;
for (int i = 0; i < 10; ++i) {
cout << real_dis(random) << ' ';
}
cout << endl;
return 0;
}
5、智能指针 unique_ptr 与 shared_ptr
作用:自动释放所指向的堆空间
(1)unique_ptr 这是个独占式的指针对象,在任何时间、资源只能被一个指针占有,当
unique_ptr离开作用域,指针所包含的内容会被释放。(只能一个指针指向该空间)。
(2)shared_ptr使用了引用计数,每一个shared_ptr的拷贝都指向相同的内存,每次拷贝都会
触发引用计数+1,每次生命周期结束析构的时候引用计数-1,在最后一个shared_ptr析构的
时候,内存才会释放。(可以多个指针指向该空间)。
(3)语法:
unique_ptr<数据类型> 变量名(地址);
unique_ptr<数据类型> 变量名 = make_unique<类型>(参数);
shared_ptr<数据类型> 变量名(地址);
shared_ptr<数据类型> 变量名 = make_shared<类型>(参数);
例子:定义一个智能指针
unique_ptr<int> u_p(new int);
unique_ptr<int> u_p1 = make_unique<int>();
shared_ptr<int> s_p(new int);
shared_ptr<int> s_p1 = make_unique<int>();
区别:如果是定义一个普通指针指向堆空间,则需要手动释放该指针才能调用析构函数,因为该指针的生命周期是delete。如果是智能指针,则不需要手动释放该指针也可以调用析构函数。
unique_ptr 例子:(独占型)
//头文件
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std;
class A {
public:
A () {cout << "A init" << endl;}
~A() {
cout << "A delete" << endl;
}
};
//测试
void test(){
cout << "test" << endl;
//使用普通指针
A *p = new A;
}
void test1()
{
cout << "test1" << endl;
//使用智能指针
unique_ptr <A> p (new A); //当test1 函数结束时会自动释放 p 所指向的堆空间
//定义一个智能指针q指向 p
unique_ptr <A> q = p; //错误的unique_ptr 是唯一的智能指针
}
int main() {
test(); //test 函数结束,普通指针不会自动释放
test1(); //test 函数结束,智能指针会自动释放
return 0;
}
shared_ptr 例子:(共享型)
//头文件
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex>
using namespace std;
class A {
public:
A() { cout << "A init" << endl; }
~A() {
cout << "A delete" << endl;
}
};
//测试
void test(){
cout << "test" << endl;
//使用普通指针
A *p = new A;
}
void test1()
{
cout << "test1" << endl;
//使用智能指针
shared_ptr <A> p(new A); //当test1 函数结束时会自动释放 p 所指向的堆空间
cout << p.get() << endl; //返回指针所指向的地址
cout << p.use_count() << endl;//返回指针的记数
//定义一个智能指针q指向 p
shared_ptr<A> q=p; //shared_ptr不是独占可以不断叠加
cout << p.get() << endl; //返回指针所指向的地址
cout << p.use_count() << endl;//返回指针的记数
}
int main()
{
test();
test1();
//自定义删除器
std::shared_ptr<int> ptr(new int, [](int *p){ cout << "delete"<<p<<endl; delete p; });
//打印指针所指向的地址
cout << ptr.get() << endl;
return 0;
}
补充:自定义删除器
std::shared_ptr <int> ptr( new int, [] (int *p) {
delete p; } );
(1)不要用普通指针初始化多个智能指针,会出现 double_free.
// int *q = new int; .
shared_ptr<int> q(new int);
shared_ptr<int> p(q);
shared_ptr<int> pp(q);
(2)尽量使用make_shared,少用new。
(3)不要delete get() 返回来的裸指针。
(4)不是new出来的空间要自定义删除器。
6、类型转换
旧式类型转换:
(数据类型)变量名; // C语言风格类型转换
新式转换:
const_cast<数据类型>(变量名);
static_cast<数据类型>(变量名);
dynamic_cast<数据类型>(变量名);
新式转换是C++的全新特性,这几个转换关键字含义如下:
const_cast : 专用于去除指针或引用的 const 属性
1.const_cast 只能作用于指针或引用类型
static_cast : 与旧式转换相近,但提供了更易于查找的语法,并能有效识别不兼容类型。
1.主要用于内置数据类型之间的相互转换。如:char ,short , int , .....
dynamic_cast : 专用于父子类的类型转换
1.将基类的指针或引用安全地转换成派生类的指针或引用,但是基类必须有虚函数
reinterpret_cast
功能:与C语言一样任意转换.
reinterpret_cast <type> (expression)
非常激进的指针类型转换,在编译期完成,可以转换任何类型的指针,所以极不安全。非极端情况不要使用。
int *ip;
char *pc = reinterpret_cast<char*>(ip);
7、lambda表达式(QT上的槽会使用)
语法:
auto func = [capture] (params) opt -> ret
{ func_body; };
func是可以当作lambda表达式的名字,作为一个函数使用
capture是捕获列表
params是参数表
opt是函数选项(mutable之类)
ret是返回值类型
func_body是函数体。
capture是捕获列表:
[]不捕获任何变量
[&]引用捕获,捕获外部作用域所有变量,在函数体内当作引用使用,可以修改值
[=]值捕获,捕获外部作用域所有变量,在函数内内有个副本使用 ,不可以修改值
[=, &a]值捕获外部作用域所有变量,按引用捕获a变量
[a]只值捕获a变量,不捕获其它变量
[this]捕获当前类中的this指针
opt选择:
int a = 0;
auto f1 = [=](){ return a; }; 值捕获a
cout << f1() << endl;
auto f2 = [=]() { return a++; }; 修改按值捕获的外部变量,error
auto f3 = [=]() mutable { return a++; }; 添加mutable 选项可以修改
lambda 表达式的应用
#include <iostream>
#include <list>
using namespace std;
int main() {
list<int> vec;
vec.push_back(10);
vec.push_back(45);
vec.push_back(4);
vec.push_back(48);
vec.sort(); //排序
//枚举
for(int i:vec){
cout << i << endl;
}
//自定义排序的规则
vec.sort( [] ( int a,int b ) { return a>b; } );
//枚举
for(int i:vec){
cout << i << endl;
}
}
demo
//利用lambada表达式实现两个数相加
#include<iostream>
using namespace std;
int main()
{
int a=10;
int b=20;
int sum;
方法1,利用capture捕获
[&a,&b,&sum](){ //或者直接[&]
sum = a+b;
}(); //()调用函数
cout << sum << endl;
方法1.1,用mutable修改值
[=]() mutable{
a = 22;
b = 33;
cout << a+b << endl;
}();
方法2,利用opt选择
auto f1 = [](int a1,int b1){ //(里面是参数列表)
int sum1 = a1 + b1;
return sum1; //返回
};
cout << f1(11,22) << endl; //像普通函数一样传参
方法2.1 外部调用
int x=1,y=2;
auto f2 = [&x,&y](){
x = 10;
y = 20;
return x+y;
};
cout << f2() << endl;
方法2.2 (传参)
[](int x1,int y1){
x1 = 10;
y1 = 20;
return x1+y1;
}(x,y);
}
总结:
1、c++的异常处理是为了提高程序员工作效率,它是先把错误放在后面解决,先完成一个个模块,因为c++有良好的封装性,一个功能有问题不会影响其他功能的实现,因此比error好。
2、c++11的新特性采用了一些新的方法,如auto,在使用迭代器的时候就不用输入很长的数据类型,它能自动转换,提高效率。如枚举循环,以前是python等脚本编程才有的特性,在2011年c++推出,减少程序员代码量。
3、lambda表达式Lambda表达式就是对函数式接口中抽象方法的实现,是对其匿名内部类的一个简写,只保留了方法的参数列表和方法体,其他的成分可以省略。不用想函数名,提高执行效率。
4、智能指针的使用方便了程序员,new完之后不用担心内存泄露,造成错误。随机数的功能适用游戏中,比较随机。final & override 提前把需要的写好,避免造成编译错误。