static_cast<>()
允许转换指针、引用及普通变量
基础类型
double b = static_cast<double>(a);
自定义类型
// 向下转换 不安全
Child* child = static_cast<Child*>(base);
// 向上转换 安全
Base& base = static_cast<Base&>(child);
仅允许在父子类之间转换,转换成其他类型会报错
dynamic_cast<>()
仅允许转换指针和引用
基础类型
不允许基础类型的转换,如下方代码是错误的:
double b = dynamic_cast<double>(a);
自定义类型
仅允许父子间转换,
允许向上转换或发生了多态的转换。
向上转换:(一直允许)
Base& base = dynamic_cast<Base&>(child);
向下转换:(不允许)
发生多态:(允许)
class Base{
public:
virtual void method();
};
class Child: public Base{
public:
virtual void method();
};
// 多态
Base* base = new Child;
Child* child = dynamic_cast<Child*>(base);
const_cast<>()
仅允许转换指针和引用
对基础类型和自定义类型的规则都相同。
去掉const
修饰
const int* p;
int* p1 = const_cast<int*>(p);
添加const
修饰
int& ref;
const int& ref1 = const_cast<const int&>(ref);
reinterpret_cast<>()
几乎不会用到。
语法相同,可以任意转换,没有安全性。
关键字:try
catch
throw
抛出异常
void fun(){
// ...
if (/* condition */){
throw error;
}
}
捕获异常
捕获某类型异常
try {
// 调用
} catch (int error) {
// 处理
} catch (char error){
// 处理
}
捕获各种异常
try {
// 调用
} catch (...) {
// 处理
}
异常必须被捕获,否则就会自动调用terminate
中断程序。
如果不想处理,可以捕获后继续抛出给上级调用者。
栈解旋
即抛出异常之前,栈上的对象都会被释放掉。
异常的接口声明
以一种声明方式告知调用者,该接口会不会抛出异常,会抛出什么类型的异常。
下面的例子表示:该接口仅抛出int
和double
类型的异常。
int fun() throw(int, double){
// ...
}
还可以声明该接口不会抛出任何异常:
int fun() throw(){
// ...
}
异常变量的生命周期
抛出引用:从抛出异常到捕获异常及处理的整个过程。
抛出值:从抛出异常到捕获异常及处理的整个过程,不过增加了开销。
抛出指针:指向的数据在捕获到并处理之前就被释放掉了。此时可以在堆区开辟空间存放,但存在内存泄漏的危险。
一般都会抛出引用
异常类
可以定义一个异常类,用来进行异常的抛出、识别和处理。
class Exception{
// ...
};
多态中的异常
class Exception{
public:
virtual string getMessage() = 0;
};
// 继承
class OutOfRangeException: public Exception{
public:
virtual string getMessage(){
return string("越界异常");
}
};
void fun(){
// ...
if(/* condition */){
// 抛出异常
throw OutOfRangeException();
}
}
int main(){
try {
fun();
} catch(Exception& error) {
// 多态
cout << error.getMessage() << endl;
}
}
C++ 标准库提供了一个异常类exception
#include
使用
捕获到的异常对象拥有一个what
方法,返回抛出者传递的字符传信息。
void fun() {
throw std::out_of_range("nmsl, cnm.");
}
int main() {
try {
fun();
} catch (std::out_of_range& error) {
cout << error.what() << endl;
}
}
也可以通过多态去捕获异常:
try {
fun();
} catch (std::exception& error) {
cout << error.what() << endl;
}
继承
继承标准库提供的异常类
class Exception {
private:
string message;
public:
Exception(string message): message(message){}
// 重写 what
virtual const char* what() const{
return this->message.c_str;
}
}
另外还有:字符串 I/O 流strstream
文件 I/O 流fstream
cout
、cerr
和clog
cout(console output)
cout 是标准输出流,除了向终端输出信息以外,还可以向磁盘文件中输出。
cerr(console error)
cerr 是标准错误流,不会经过缓冲区,也不允许向文件中输出。
clog(console log)
clog 也是标准错误流,不过它会经过缓冲区。
几个重要接口:
cin.get(); // 输入一个字符
cin.get(char* buffer, int length); // 输入一个字符串
cin.getline(char*); // 输入一个字符串
cin.ignore(char); // 忽略即将读入的字符
cin.peek(); // 查看/偷窥 即将读入的字符
cin.fail(); // 获取错误状态
cin.clear(); // 重置错误状态
cin.sync(); // 清空缓冲区(存在问题)
cin.get
int cin.get();
一次读一个字符,与getchar()
没什么区别。
istream& get(char* buffer, int length);
一次读一串字符,存入连续的内存中。不读末尾换行符\n
,将其留在缓冲区,且遇到空格不会停止。
cin.ignore
istream& ignore(int count = 1);
丢弃/忽略马上读入的count
个字符。
istream& ignore(int count, char c);
丢弃字符,直到数量达到 count
个,或当期丢弃的字符与c
相同。
cin.getline
istream& getline(char* buffer, int length);
一次读一串字符,存入连续的内存中。读末尾换行符\n
,只不过不会读入内存中,而是直接丢弃,且遇到空格不会停止。
cin.peek
int peek();
偷窥,即读入一个字符,然后放回去。
cin.putback
istream& putback(char c);
将 c 中存放的字符放到输入缓冲区头部,即读入的下一个字符处。
cin.fail
bool fail();
用于获取 cin 的错误状态。返回 0 代表没有发生错误,1 代表发生了错误。
例如用户输入一个 0 - 9 以外的字符,而我通过一个 int 来接收它,此时就会出错。
cin.clear
用于重置 cin 的错误状态。
cin.sync
用于清空缓冲区,不过在某些编译器上的实现并非清空缓冲区。
例如 vc++ 2019。
想要清空缓冲区可以这样做:
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
几个接口:(了解即可)
cout.flush(); // 清空输出缓冲区
cout.put(char c); // 向缓冲区写一个字符
cout.write(char* buffer, int length); // 向缓冲区写一个字符串
格式化输出
cout.width(int width); // 输出长度
cout.fill(char c); // 多余宽度通过 c 填充
cout.setf(ios::_Fmtflags flags); // 设置格式
cout.unsetf(ios::_Fmtflags flags); // 写在格式
cout.precision(int n); // 设置实数精度为 n
关于cout.setf
,其还可以通过控制符来格式化输出:
包含一个头文件:
#include
cout << setw(int width) // 输出长度
<< setfill(char c) // 多余宽度通过 c 填充
<< setiosflags(ios::_Fmtflags flags) // 设置格式
<< hex // 16 // 设置格式
<< oct // 8 // 设置格式
<< dec // 10 // 设置格式
<< endl;
其较常用的参数如下:
标志 | 说明 |
---|---|
ios::left | 输出左对齐 |
ios::right | 输出右对齐 |
ios::internal | 符号位左对齐,数值右对齐,中间填充 |
ios::dec | 十进制 |
ios::oct | 八进制 |
ios::hex | 十六进制 |
ios::showbase | 输出前导符 ’0x‘ 或 ’0‘ |
ios::showpointer | 强制输出浮点数的小数点和尾数 0 |
ios::showpos | 对正数显示 + 号 |
ios::scientific | 浮点数以科学计数法的格式输出 |
ios::fixed | 浮点数以定点格式输出(小数形式) |
ios::unitbuf | 每次输出之后刷新所有的流 |
ios::stdio | 每次输出之后清除 stdout stderr |
头文件:
#include
打开文件
实例化时:
ofstream ofs(const char* filename,ios::openmode _Mode );
实例化后:
ofstream ofs;
ofs.open(const char* filename,ios::openmode _Mode );
检验:
if (!ofs.is_open()){
// ...
}
关于 openmode:
方式 | 说明 |
---|---|
ios::in | 输入 |
ios::out | 输出(默认方式),若文件已存在则清空内容 |
ios::app | 输出,追加在末尾 |
ios::ate | 打开已有文件,文件指针指向文件尾 |
ios::trunc | 打开一个文件,文件存在则清空数据,文件不存在这建立新文件 |
ios::binary | 以二进制方式打开文件 |
ios::nocreate | 打开已有文件,若文件不存在则打开失败 |
ios::noreplace | 建立一个文件,若文件已存在则打开失败 |
ios::in | ios::out | 可读可写 |
ios::out | ios::binary | 以二进制方式打开一个输出文件 |
ios::in | ios::binary | 以二进制方式打开一个时输入文件 |
写文件
ofstream ofs("./write.txt", ios::out);
ofs << "test" << endl;
ofs.close();
读文件
ifstream ifs("./read.txt", ios::in);
char buf[1024];
while (ifs >> buf) {
cout << buf << endl;
}
ifstream ifs("./read.txt", ios::in);
char buf[1024];
while (!ifs.eof()) {
ifs.getline(buf, sizeof(buf));
cout << buf << endl;
}
ifstream ifs("./read.txt", ios::in);
char c;
while (!ifs.eof()) {
c = ifs.get();
cout << c;
}
// 或者
while ( (c = ifs.get()) != EOF ) {
cout << c;
}
头文件:
#include
可以向一个流中传递数据和拿取数据。
stringstream stream;
// 获取一个流
stream << "123";
// 向流中推送数据
stream >> var;
// 向流流中拿取数据
应用:将任意基础类型进行转换
template <class resultType, class paramType >
resultType transform(paramType input) {
stringstream stream;
resultType output;
stream << input;
stream >> output;
return output;
}
例如:
int output = transform<int>("123");
cout << output;
// > 123