拷贝会有两个场景:
- 拷贝构造函数
- 赋值运算符重载
因此只需要将这两个禁止掉即可
在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用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用,但是不能用于两个不相关的类型进行转换
int main()
{
double d = 11.11;
int a = static_cast<int>(d);
cout<<a<<endl;
return 0;
}
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属性方便赋值
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会先检查是否能转换成功,能成功则转换,不能则返回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;
}
流:是对一种有序连续且具有方向性的数据( 其单位可以是bit,byte,packet )的抽象描述
C++流是指信息从外部输入设备(如键盘)向计算机内部(如内存)输入和从内存向外部输出设备(显示器)输出的过程
C++标准库提供了4个全局流对象cin、cout、cerr、clog,使用cout进行标准输出,即数据从内存流向控制台(显示器)。使用cin进行标准输入即数据通过键盘输入到程序中,同时C++标准库还提供了cerr用来进行标准错误的输出,以及clog进行日志的输出
- cin为缓冲流。键盘输入的数据保存在缓冲区中,当要提取时,是从缓冲区中拿。如果输入错了,必须在回车之前修改,如果回车键按下就无法挽回了。只有把输入缓冲区中的数据取完后,才要求输入新的数据
- 输入的数据类型必须与要提取的数据类型一致
- 空格和回车都可以作为数据之间的分格符,所以多个数据可以在一行输入,也可以分行输入
- 如果是字符型和字符串,则空格(ASCII码为32)无法用cin输入,字符串中也不能有空格,回车符也无法读入
- cin和cout可以直接输入和输出内置类型数据,对于自定义类型,如果要支持cin和cout的标准输入输出,需要对<<和>>进行重载
C++根据文件内容的数据格式分为二进制文件和文本文件
- ifstream ifile(只输入用)
ofstream ofile(只输出用)
fstream iofile(既输入又输出用)- 使用文件流对象的成员函数打开一个磁盘文件,使得文件流对象和磁盘文件之间建立联系
- 使用提取和插入运算符对文件进行读写操作,或使用成员函数进行读写
C++文件流的优势就是可以对内置类型和自定义类型,都使用一样的方式,去流插入和流提取数据