特殊类
不能被拷贝的类
拷贝会有两个场景:
- 拷贝构造函数
- 赋值运算符重载
因此只需要将这两个禁止掉即可
在C++98中可以将这两个函数设为私有,或者只声明不定义
在C++11中可以在默认成员函数后面加上 =delete 表示将该默认成员函数删除
class A { A(const A&) =delete; A& operator=(const A&) =delete; };
只能在堆上创建的类
可以分两个步骤:
- 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
- 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
class A { public: static A* create() { return new A; } private: A() {} A(const A&) =delete; };
只能在栈上创建的类
和只在堆上创建一样,只需要将静态方法返回创建对象即可,并且禁止new
class A { public: static A create() { return A(); } void* operator new() = delete; void operator delete(void* p) = delete; private: A() {} A(const A&) =delete; };
不能被继承的类
在C++98中需要将构造函数设为私有,这样派生类调用不到基类的构造函数就不能继承
在C++11中可以在类后面加上 final关键字,表示该类不可继承
class A final { };
只能创建一个对象的类
这中类就是单例模式,一个类只能创建一个对象,这种单例模式可以保证该类只有一个实例。
这种模式可以有两种实现的方法
饿汉模式
不管用不用得到这个类,只要在程序启动时就创建一个该类的对象
class A { public: static A* GetInstance() { return &m_instance; } private: // 构造函数私有 A(){}; // 取消拷贝和赋值 A(A const&) = delete; A& operator=(A const&) = delete; static A m_instance; }; // 在程序入口就完成对象初始化 A A::m_instance;
懒汉模式
和饿汉模式正好相反,在用到的时候再创建对象
类型转换
为什么需要四种类型转换
C语言的转换风格虽然很简单,但是有不少的缺点
- 隐式类型转化有些情况下可能会出现精度丢失的问题
- 显式类型转换会将所有情况混合在一起,导致代码不够清晰
因此C++就提出了自己的转换风格,并且C++兼容C语言,所以C++中也可以使用C语言的风格
static_cast
static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用,但是不能用于两个不相关的类型进行转换
int main() { double d = 11.11; int a = static_cast<int>(d); cout<<a<<endl; return 0; }
reinterpret_cast
reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型
int main() { double d = 11.11; int a = static_cast<int>(d); cout << a << endl; // 这里使用static_cast会报错 int *p = reinterpret_cast<int*>(a); return 0; }
const_cast
const_cast最常用的用途就是删除变量的const属性方便赋值
int main() { const int a = 2; int* p = const_cast< int*>(&a ); *p = 3; cout<<a <<endl; return 0; }
dynamic_cast
dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针/引用 -> 父类指针/引用 (不需要准换,赋值兼容)
向下转型:父类对象指针/引用 -> 子类指针/引用 (需要用dynamic_cast转型)
dynamic_cast只能用于父类含有虚函数的类
dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0
class A { public : virtual void f(){} }; class B : public A {}; void fun (A* pa) { // dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回 B* pb1 = static_cast<B*>(pa); B* pb2 = dynamic_cast<B*>(pa); cout<<"pb1:" <<pb1<< endl; cout<<"pb2:" <<pb2<< endl; } int main () { A a; B b; fun(&a); fun(&b); return 0; }
IO流
什么是流
流:是对一种有序连续且具有方向性的数据( 其单位可以是bit,byte,packet )的抽象描述
C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程
C++标准IO流
C++标准库提供了4个全局流对象cin、cout、cerr、clog,使用cout进行标准输出,即数据从内存流向控制台(显示器)。使用cin进行标准输入即数据通过键盘输入到程序中,同时C++标准库还提供了cerr用来进行标准错误的输出,以及clog进行日志的输出
- cin为缓冲流。键盘输入的数据保存在缓冲区中,当要提取时,是从缓冲区中拿。如果输入错了,必须在回车之前修改,如果回车键按下就无法挽回了。只有把输入缓冲区中的数据取完后,才要求输入新的数据
- 输入的数据类型必须与要提取的数据类型一致
- 空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输入
- 如果是字符型和字符串,则空格(ASCII码为32)无法用cin输入,字符串中也不能有空格,回车符也无法读入
- cin和cout可以直接输入和输出内置类型数据,对于自定义类型,如果要支持cin和cout的标准输入输出,需要对<<和>>进行重载
C++文件IO流
C++根据文件内容的数据格式分为二进制文件和文本文件
- ifstream ifile(只输入用)
ofstream ofile(只输出用)
fstream iofile(既输入又输出用)
- 使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系
- 使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写
C++文件流的优势就是可以对内置类型和自定义类型,都使用一样的方式,去流插入和流提取数据