c++学习笔记
static和virtual
静态变量:
- 声明类的(而不是对象的变量)变量可以使用static const int NUM = 10,与enum {NUM = 10}功能类似。
- 静态函数:作用域为定义函数的cpp文件,可以避免与项目中同名函数冲突。
构造函数中使用了new,则需要显式定义:析构函数、复制构造函数和重载赋值运算符。
基类的标配:虚析构函数。
对于派生类使用new的情况,在显式定义如下函数时需要引入基类的相应函数来处理基类的属性:
- 派生类析构函数自动调用基类的析构函数,因此派生类析构函数的职责是对派生类构造函数执行的工作进行清理。
- 派生类复制构造函数需要调用基类复制构造函数来处理共享的基类属性。
baseDMA::baseDMA(const baseDMA & rs) { label = new char[std::strlen(rs.label)+1]; std::strcpy(label, rs.label); rating = rs.rating; } hasDMA::hasDMA(const hasDMA & hs):baseDMA(hs) { style = new char[std::strlen(hs.style)+1]; std::strcpy(style, hs.style); }
- 派生类赋值构造函数可以通过显式调用基类赋值运算符来完成对继承得到的基类属性的赋值
hasDMA & hasDMA::operator=(const hasDMA & hs) { if(this == &hs) return *this; baseDMA::operator=(hs);//赋值基类的属性 delete style; style = new char[std::strlen(hs.style)+1]; std::strcpy(style,hs.style); return *this; } ``
友元函数
通过强制类型转换实现调用基类的友元函数
os<<(const baseDMA &)hs;
C++11新增的两个特殊方法:移动构造函数,移动赋值运算符,继承构造函数的机制
作为“is-a”模型一部分,派生类继承基类的数据成员和大部分方法,但不继承构造函数、析构函数和赋值运算符。
多态公有继承
有两种重要的机制可用于实现多态公有继承:
- 在派生类中重新定义基类的方法
- 使用虚方法
虚函数要点。如果方法是通过引用或指针而不是对象调用,它将确定使用哪一种方法。如果没有关键字virtual,程序将根据引用类型或者指针类型选择方法;如果使用了virtual,程序将根据引用或者指针类型指向的对象选择方法。
基类的析构函数必须为虚析构函数。
纯虚函数
使用了纯虚函数的类为抽象类,不能用来定义对象。纯虚析构函数必须要给出定义;其他纯虚函数不必给出定义。
异常
运行阶段错误预防方案。
- 异常终止。调用abort()函数,该函数原型包含在cstdlib中。该方法需要程序员检查……。
- 返回错误码。依赖函数的返回值来指出问题。该方法需要程序员检查……。
- 异常机制。
看一下异常机制的例子
// error3.cpp -- using an exception
#include <iostream>
double hmean(double a, double b);
int main()
{
double x, y, z;
std::cout << "Enter two numbers: ";
while (std::cin >> x >> y)
{
try { // start of try block
z = hmean(x,y);
} // end of try block
catch (const char * s) // start of exception handler
{
std::cout << s << std::endl;
std::cout << "Enter a new pair of numbers: ";
continue;
} // end of handler
std::cout << "Harmonic mean of " << x << " and " << y
<< " is " << z << std::endl;
std::cout << "Enter next set of numbers <q to quit>: ";
}
std::cout << "Bye!\n";
return 0;
}
double hmean(double a, double b)
{
if (a == -b)
throw "bad hmean() arguments: a = -b not allowed";
return 2.0 * a * b / (a + b);
}
stdexcept异常类
头文件stdexcept定义了其它几个异常类。构造函数接受一个string对象作为参数,该参数提供了方法what()以C-风格字符串方式返回的字符数据。
logic_error:
- domain_error,参数定义域错误;
- invalid_argument,给函数传递了意料外的值;
- length_error,用于指出没有足够空间来执行所需操作;
- out_of_bounds,通常用于指示索引错误。
和系列描述了可能在运行期间发生但难以预料和防范的错误,runtime_error:
- range_error,值域错误;
- overflow_error;
- underflow_error。
bad_alloc:
对于使用new导致的内存分配问题,c++最新处理方式是让new引发bad_alloc异常。头文件new包含bad_alloc类的声明,后者从exception类公有派生而来。但以前,当new无法正常分配请求的内存时,返回空指针。
c++标准提供了一种方式,使得new在失败时返回空指针。
int * pi = new (std::nothrow) int;
int * pa = new (std::nothrow) int[500];
bad_exception:
异常被引发后,两种情况下会导致问题,首先如果它是在带异常规范的函数中引发的,则必须与规范列表中的某种异常匹配,否则称为意外异常,默认情况下导致程序以异常终止。如果函数不是在带异常规范的函数中引发的,则必须捕获它,当没有捕获时,异常被称为未捕获异常,这种情况下默认也是使程序异常终止。然而可以修改程序对意外异常和未捕获异常的反应。
未捕获异常不会导致程序立刻异常终止,而是按照下图进行处理。
意外异常发生时,程序将调用unexpected()函数,具体如下流程图。
set_unexpected(unexpected_handler)中unexpected_handler函数可以
- 调用terminate()函数(默认)、abort()或exit()来终止程序;
- 引发异常
- 如果新引发的异常与异常规范匹配,则程序catch该异常;
- 如果不匹配,且异常规范中没有包括std::bad_expection类型,则程序调用terminate()函数
- 如果不匹配但异常规范中包括std::bad_expection类型,则不匹配的异常被bad_expection替代。
bad_cast:
继承自exception类,包含在typeinfo头文件中。使用dynamic_cast运算符,当处理的对象为指针时,要么正确转化返回相应的指针类型,要么失败返回空指针;当处理引用对象时,要么正确转化,要么引发bad_cast异常。
bad_typeid:
如果typeid(*pg)中的pg为空指针,程序将引发bad_typeid异常。头文件为typeinfo
警告:RTTI只适用于包含虚函数的类
C++各种转换的区别
dynamic_cast和static_cast:dynamic_cast只允许沿类层次结构向上转换,而static_cast运算符允许向上转换和向下转换。static_cast还允许枚举和整形之间以及数值类型之间的转换。
reinterpret_cast