c++(类型转换+IO)[30]

类型转换

意义相近的类型------隐式类型转换
意义不想近的类型,值转换后有意义------显示的强制类型转换

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(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代表常变量,可以被间接修改。

监视窗口:
c++(类型转换+IO)[30]_第1张图片
原因:编译器优化认为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无法进行安全的向下转型,会导致编译错误。

c++(类型转换+IO)[30]_第2张图片
父类指针指向子类,虽然可以通过强转成子类来访问子类对象,但是会越界风险
c++(类型转换+IO)[30]_第3张图片
使用dynamic_cast:
c++(类型转换+IO)[30]_第4张图片
c++(类型转换+IO)[30]_第5张图片
上述代码若使用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;
}

c++(类型转换+IO)[30]_第6张图片

RTTI

RTTI(Run-Time Type Information,运行时类型信息)是C++中的一项特性,用于在运行时获取对象的类型信息。RTTI主要通过两个关键字来实现:dynamic_cast和typeid。

  1. dynamic_cast:dynamic_cast是一个类型转换操作符,用于进行安全的向下转型。它在进行转换时会进行运行时类型检查,如果转换不安全,会返回空指针或抛出std::bad_cast异常。dynamic_cast需要基类至少声明一个虚函数,通过虚函数表来获取对象的实际类型信息。

  2. typeid:typeid是一个运算符,用于获取对象的类型信息。它返回一个std::type_info对象,包含了对象的实际类型信息。可以使用typeid来比较两个对象的类型是否相同。

RTTI的主要应用场景是在面向对象的程序中,需要根据对象的实际类型进行动态的类型判断和类型转换。通过RTTI,可以在运行时获取对象的类型信息,从而实现多态性和动态绑定。

需要注意的是,RTTI的使用需要谨慎,因为它可能引入一些运行时开销,并且在某些情况下可能会导致设计上的问题。在一些情况下,可以通过使用虚函数和多态性来避免使用RTTI。

c++(类型转换+IO)[30]_第7张图片

IO

c++(类型转换+IO)[30]_第8张图片
c++(类型转换+IO)[30]_第9张图片
分割输入字符:
c++(类型转换+IO)[30]_第10张图片
c++(类型转换+IO)[30]_第11张图片
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 
#include 

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()等。

c++(类型转换+IO)[30]_第12张图片

c++(类型转换+IO)[30]_第13张图片
提取日期类前提:日期类重载了流提取

你可能感兴趣的:(C++,c++)