目录
运算符重载
C++里的函数名相同、函数的参数个数、类型、顺序不同,可以发生重载(函数重载)
C++对可以对基本的运算符进行重载,重载之后就可以用于非基本类型的数据的运算,所以不能创建新的运算符来重载,运算符重载的实质是函数重载,遵循函数重载的选择原则,重载后的运算符依旧遵循原运算符的结合性和优先级,而且不能有参数,重载的运算符只能用于用户自定义类型
不能被重载的运算符:
. | .* | -> | :: | sizeof | ?: |
重载的方式:
——关键字:operator
1.普通成员函数的运算符重载
格式:
//声明格式:
返回值类型 operator 被重载的运算符 (参数列表);
//定义格式:
返回值类型 operator 被重载的运算符 (参数列表)
{
//函数体
}
//例如:
class Test
{
public:
int a = 10;
int b = 20;
//重载“+”实现两个对象中a相加并且b相加
Test operator +(const Test& obj)
{
Test t;
t.a = this->a - obj.a;
t.b = this->b - obj.b;
return t;
}
};
int main()
{
Test t1,t2,t3;
t3 = t1 + t2;
cout << "t3.a=" << t3.a << endl;
cout << "t3.b=" << t3.b << endl;
return 0;
}
2.友元函数的运算符重载
格式:
//声明格式:
friend 返回值类型 operator 被重载的运算符(参数列表);
//定义:
返回值类型 operator 被重载的运算符(参数列表)
{
//函数体
}
//例如:
class Test
{
public:
int a = 10;
int b = 100;
friend Test operator -(const Test& obj1, const Test& obj2);//友元函数声明
};
//友元函数
Test operator -(const Test& obj1,const Test &obj2)
{
Test t;
t.a = obj1.a - obj2.a;
t.b = obj1.b - obj2.b;
return t;
}
int main()
{
Test t1,t2,t3;
t3 = t1 - t2;
cout << "t3.a=" << t3.a << endl;
cout << "t3.b=" << t3.b << endl;
return 0;
}
3.一般函数的成员运算符重载
格式:
//声明格式:
函数返回值类型 operator 被重载的运算符(参数列表)
{
//函数体
}
//例如:
class Test
{
public:
int a = 100;
int b = 10;
};
Test operator *(const Test& obj1, const Test& obj2)
{
Test t;
t.a = obj1.a * obj2.a;
t.b = obj1.b * obj2.b;
return t;
}
int main()
{
Test t1,t2,t3;
t3 = t1 * t2;
cout << "t3.a=" << t3.a << endl;
cout << "t3.b=" << t3.b << endl;
return 0;
}
继承与多态
继承
一个类共享到了其他几个类的特征和行为,通俗点说就是子类(派生类)继承了父类(基类)的一部分成员变量以及成员方法
class 新的类名(子类):继承的权限 已经存在的类(父类)
{
//类体
};
继承的权限
继承的权限有public,protected,private
多继承
一个子类继承了多个父类的特征和方法
class 新的类名(子类):继承的权限1 类1(父类),继承的权限2 类2(父类)
{
//类体
};
多态
一种接口,多种方法
构成多态性:
1.两个类具有继承关系
2.子类中必须重写父类的方法(子类方法中的内容与父类中的方法可以不一样)
3.父类中的方法必须是虚函数
多态调用的时机:当父类的指针或者是引用指向子类时
虚函数:虚函数相当于是一个普通的成员函数,只不过在该函数前加了一个关键字“virtual”
//声明格式:
virtual 返回值类型 函数名(参数列表);
//定义格式:
virtual 返回值类型 函数名(参数列表)
{
//函数体
}
静态绑定:函数的定义和调用在编译阶段就确定了,称之为静态绑定
动态绑定:函数的定义和调用在运行阶段才确定,称之为动态绑定(虚函数)
抽象类
抽象类只是描述事物具备一些特征和行为,但是不能实例化,只能被继承行为和方法
抽象类中一定包含纯虚函数,在虚函数的声明后面加上 = 0就是一个纯虚函数
//抽象类声明格式:
class 类名
{
virtual 返回值类型 函数名(参数列表)=0;//纯虚函数声明
};
虚析构函数
虚析构函数就是在析构函数前加上一个关键字virtual,那就表明该析构函数为虚析构函数
作用:解决父类指针指向子类对象的指针释放时,只会释放父类指针,而不释放子类子类指针的问题
//格式:
virtual ~函数名()
{
//函数体
}
注:
1.两个类需要有继承关系
2.是将父类的析构函数声明虚析构函数
3.基类指针指向子类的对象
虚继承
虚继承就是在继承权限前加上关键字virtual
作用:解决多重继承(菱形继承)造成的二义性问题
//格式:
class 类1:virtual 继承权限 类2
{
//代码块
}
异常
异常:是C++中一种容错机制,是一个错误处理系统。可以将问题的发现与问题的处理分离
——关键字:throw 、try、catch
//异常关键字thorw,try,catch
//有一个计算除法的函数,如果用户使用除法的时候,输入了分母0,就抛出异常,同时进行处理
//例如:
int divfunc(int a, int b)
{
if (b == 0)
{
throw 0;//抛出异常
}
else
{
return a / b;
}
}
int main3()
{
try//检测异常
{
divfunc(9, 0);
}
catch (int)//处理异常
{
cout << "分母不能为0" << endl;
}
return 0;
}
1.如果确定异常已经产生,但是用户没有去写捕获该异常的catch,那么程序的外层继续捕获该异常,如果还是没有捕获到,那么最后就调用windows的terminate来终止程序
2.try模块后面必须紧跟着一个catch块或者是多个catch块
3.产生的异常首先是匹配try后紧跟着的第一个catch块,如果没有匹配,继续往下一个catch进行匹配,如果还是没有捕获到,那么最后就调用windows的terminate来终止程序
4.catch(...)它是用于捕获所有异常
5.try...catch块可以发生嵌套
自定义异常:需要继承C++提供的所有异常的基类exception,可以在自定义的异常类重写what()方法,用于查看产生的异常的类型
//自定义异常类
class MyException :public exception
{
public:
const char* str;
MyException(const char* obj)
{
str = obj;
}
const char* what()
{
return str;
}
};
int div_func(int a,int b)
{
if (b == 0)
{
throw MyException("分母不能为0");
}
return a / b;
}
int main4()
{
try
{
div_func(5, 0);
}
catch (MyException&a)
{
cout << "产生异常:" <<a.what() << endl;
}
return 0;
}
标椎异常:
std::exception | 该异常是所有标准 C++ 异常的父类 |
std::bad_alloc | 该异常可以通过 new 抛出 |
std::bad_cast | 该异常可以通过 dynamic_cast 抛出 |
std::bad_exception | 这在处理 C++ 程序中无法预期的异常时非常有用 |
std::bad_typeid | 该异常可以通过 typeid 抛出 |
std::logic_error | 理论上可以通过读取代码来检测到的异常 |
std::domain_error | 当使用了一个无效的数学域时,会抛出该异常 |
std::length_error | 当创建了太长的 std::string 时,会抛出该异常 |
std::out_of_range | 参数超出有效范围 |
std::runtime_error | 理论上不可以通过读取代码来检测到的异常 |
std::overflow_error | 当发生数学上溢时,会抛出该异常 |
std::range_error | 当尝试存储超出范围的值时,会抛出该异常 |
std::underflow_error | 当发生数学下溢时,会抛出该异常 |
std::invalid_argument | 当使用了无效的参数时,会抛出该异常 |