类型转换
意义相近的类型------隐式类型转换
意义不想近的类型,值转换后有意义------显示的强制类型转换
static_cast
任何隐式类型的转换,非多态类型的转换(静态转换),意义相近的转换。
用于常见的隐式类型转换,如数值类型之间的转换、指针和引用之间的转换,以及基类指针向派生类指针的转换。在进行转换时,static_cast会在编译时进行类型检查,如果转换是不安全的,会发出警告
int main()
{
double d = 12.34;
int a = static_cast<int>(d);
cout << a << endl;
return 0;
}
reinterpret_cast
不相关类型的转换
用于进行底层的类型转换,可以将一个指针或引用转换为其他类型的指针或引用,甚至可以将指针转换为整数类型。reinterpret_cast在进行转换时不会进行类型检查,因此需要谨慎使用,只在确保转换是安全的情况下使用。
int main()
{
double d = 12.34;
int a = static_cast<int>(d);
cout << a << endl;
int* p = &a;
//不支持
//int adress = static_cast<int>(p);
int adress = reinterpret_cast<int>(p);
return 0;
}
const_cast
用于去除const属性,可以将const指针或引用转换为非const指针或引用。const_cast只能用于修改指针或引用的const属性,而不能修改其他属性,如对象的值。使用const_cast需要注意,如果修改了原本被声明为const的对象,可能会导致未定义行为。
const int a = 2;
int* p = const_cast<int*>(&a);
*p = 3;
cout << a << endl;
cout << *p << endl;
c++中const代表常变量,可以被间接修改。
监视窗口:
原因:编译器优化认为const不会被修改,将a加载到寄存器当中。读取a时去寄存器当中读。内存当中的a实际是被修改的
使用volatile关键字取消编译器优化
volatile const int a = 2;
int* p = const_cast<int*>(&a);
*p = 3;
cout << a << endl; //3
cout << *p << endl; //3
dynamic_cast
用于进行安全的向下转型,即将基类指针或引用转换为派生类指针或引用。dynamic_cast在进行转换时会进行运行时类型检查,如果转换不安全,会返回空指针或抛出std::bad_cast异常。dynamic_cast只能用于有继承关系的类之间进行转换。
当使用dynamic_cast进行向下转型时,如果基类没有声明任何虚函数,就无法使用dynamic_cast进行安全的转换,会导致编译错误。这是因为dynamic_cast在进行转换时需要运行时类型信息(RTTI)来判断是否可以进行转换,而运行时类型信息是通过虚函数表来实现的。
在没有虚函数的情况下,基类对象的指针或引用无法获取到运行时类型信息,因此dynamic_cast无法进行安全的转换。编译器会在这种情况下给出编译错误,提示无法进行转换。
要想使用dynamic_cast进行安全的向下转型,基类必须至少声明一个虚函数。这样,在派生类中重写了该虚函数后,dynamic_cast才能通过虚函数表获取到运行时类型信息,从而进行安全的转换。
总结起来,如果基类没有声明任何虚函数,dynamic_cast无法进行安全的向下转型,会导致编译错误。
父类指针指向子类,虽然可以通过强转成子类来访问子类对象,但是会越界风险
使用dynamic_cast:
上述代码若使用c语言的强转则会出现(父类-子类:子类-父类)全部转换成功,父类的指向子类则会出现越界。
class A
{
public:
virtual void f(){}
};
class B : public A
{};
// RAII
// RTTI
void fun(A* pa, const string& s)
{
cout <<"pa指向"<<s << endl;
// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
B* pb1 = (B*)pa; // 不安全的
B* pb2 = dynamic_cast<B*>(pa); // 安全的
cout << "[强制转换]pb1:" << pb1 << endl;
cout << "[dynamic_cast转换]pb2:" << pb2 << endl << endl;
}
int main()
{
A a;
B b;
fun(&a, "指向父类对象");
fun(&b, "指向子类对象");
cout << typeid(a).name() << endl;
decltype(a) aa;
function<bool(int,int)> gt = [](int x, int y){return x > y; };
set<int, decltype(gt)> s;
return 0;
}
RTTI
RTTI(Run-Time Type Information,运行时类型信息)是C++中的一项特性,用于在运行时获取对象的类型信息。RTTI主要通过两个关键字来实现:dynamic_cast和typeid。
-
dynamic_cast:dynamic_cast是一个类型转换操作符,用于进行安全的向下转型。它在进行转换时会进行运行时类型检查,如果转换不安全,会返回空指针或抛出std::bad_cast异常。dynamic_cast需要基类至少声明一个虚函数,通过虚函数表来获取对象的实际类型信息。
-
typeid:typeid是一个运算符,用于获取对象的类型信息。它返回一个std::type_info对象,包含了对象的实际类型信息。可以使用typeid来比较两个对象的类型是否相同。
RTTI的主要应用场景是在面向对象的程序中,需要根据对象的实际类型进行动态的类型判断和类型转换。通过RTTI,可以在运行时获取对象的类型信息,从而实现多态性和动态绑定。
需要注意的是,RTTI的使用需要谨慎,因为它可能引入一些运行时开销,并且在某些情况下可能会导致设计上的问题。在一些情况下,可以通过使用虚函数和多态性来避免使用RTTI。
IO
分割输入字符:
Ctrl+z 加回车(隐式类型转换cin>>str为bool的false)结束此程序
自定义类型转换为内置类型
在C++中,可以通过重载类型转换运算符(type conversion operator)来实现自定义类型到内置类型的转换。类型转换运算符是一种特殊的成员函数,用于将一个类类型转换为另一个类型。
以下是一个示例,演示了如何将自定义的类类型转换为内置类型int:
class MyInt {
private:
int value;
public:
// 构造函数
MyInt(int v) : value(v) {}
// 类型转换运算符
operator int() const {
return value;
}
};
int main() {
MyInt myInt(42);
int intValue = myInt; // 自动调用类型转换运算符将MyInt转换为int
std::cout << intValue << std::endl; // 输出: 42
return 0;
}
在上面的示例中,类MyInt定义了一个类型转换运算符operator int(),它将MyInt类型转换为int类型。当我们将一个MyInt对象赋值给int类型的变量时,编译器会自动调用这个类型转换运算符,将MyInt转换为int。
需要注意的是,类型转换运算符通常被定义为成员函数,并且没有参数(除了可能的const和引用修饰符)。它们没有返回类型,但返回的值类型就是要转换的目标类型。
需要谨慎使用类型转换运算符,因为它们可能导致意外的隐式类型转换,可能会降低代码的可读性和可维护性。
ifstream
ifstream
是C++标准库中的一个输入文件流类,用于从文件中读取数据。它是istream
类的派生类,继承了istream
类中的输入操作。
使用ifstream
类,你可以打开一个文件,从文件中读取数据,并进行相应的操作。以下是一个简单的示例,演示了如何使用ifstream
读取文件内容:
#include <iostream>
#include <fstream>
int main() {
std::ifstream file("example.txt"); // 打开文件
if (file.is_open()) { // 检查文件是否成功打开
std::string line;
while (std::getline(file, line)) { // 逐行读取文件内容
std::cout << line << std::endl; // 输出每一行内容
}
file.close(); // 关闭文件
} else {
std::cout << "无法打开文件" << std::endl;
}
return 0;
}
在上面的示例中,我们首先创建了一个ifstream
对象file
,并使用文件名"example.txt"来打开文件。然后,我们使用is_open()
函数检查文件是否成功打开。如果文件成功打开,我们使用getline()
函数逐行读取文件内容,并输出每一行的内容。最后,我们使用close()
函数关闭文件。
需要注意的是,ifstream
类的对象在使用完毕后,应该调用close()
函数关闭文件。另外,ifstream
类还提供了其他的成员函数,用于读取不同类型的数据,如read()
、get()
、getline()
等。
提取日期类前提:日期类重载了流提取