特殊类的设计、C++四种类型转换

🐶博主主页:@ᰔᩚ. 一怀明月ꦿ 

❤️‍🔥专栏系列:线性代数C初学者入门训练题解CC的使用文章「初学」C++

🔥座右铭:“不要等到什么都没有了,才下定决心去做”

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

目录

特殊类的设计

设计一个类,不能被拷贝

设计一个类,只能在堆上创建对象

设计一个类,只能在栈上创建对象

类型转换

 为什么C++需要四种类型转换

static_cast

reinterpret_cast

const_cast

dynamic_cast


特殊类的设计

设计一个类,不能被拷贝

拷贝只会发生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。

C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上

=delete,表示让编译器删除掉该默认成员函数

class CopyBan
{
// ...
CopyBan(const CopyBan&)=delete;
CopyBan& operator=(const CopyBan&)=delete;
//...
};
设计一个类,只能在堆上创建对象

要设计一个类,只能在堆上创建对象,你可以通过私有化构造函数和析构函数,并提供一个静态公有的静态成员函数来实现。这个静态成员函数负责在堆上分配对象,并返回指向该对象的指针。然后,你可以使用 delete 关键字来释放对象。

#include <iostream>
class HeapOnly {
private:
    // 私有化构造函数和析构函数
    HeapOnly() {}
    ~HeapOnly() {}
public:
    // 静态成员函数,用于在堆上创建对象
    static HeapOnly* createObject() {
        return new HeapOnly();
    }
    
    // 静态成员函数,用于在堆上创建对象
    static void destroyObject(HeapOnly* obj) {
        delete obj;
    }
    // 其他公有成员函数
    void showMessage() {
        std::cout << "HeapOnly object created on heap!" << std::endl;
    }
};

int main() {
    // 在堆上创建对象
    HeapOnly* obj = HeapOnly::createObject();

    // 使用对象
    obj->showMessage();

    // 释放对象
    HeapOnly::destroyObject(obj);

    
    return 0;
}

在这个示例中,HeapOnly 类的构造函数和析构函数被私有化,防止在栈上或全局上创建对象。而 createObject 静态成员函数允许在堆上创建对象,并返回指向该对象的指针。在 main 函数中,我们通过调用 createObject 函数在堆上创建了对象,然后使用对象的成员函数,并最后使用 delete 关键字释放了对象。

设计一个类,只能在栈上创建对象

如果一个类重载了new,那么用new创建类对象,就会调用这个重载new

class StackOnly
{
public:
static StackOnly CreateObj()
{
return StackOnly();
}
// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉
// StackOnly obj = StackOnly::CreateObj();
// StackOnly* ptr3 = new StackOnly(obj);
void* operator new(size_t size) = delete;
void operator delete(void* p) = delete;
private:
StackOnly()
:_a(0)
{}
private:
int _a;
};

类型转换

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。
1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
2. 显式类型转化:需要用户自己处理

void Test ()
{
int i = 1;
// 隐式类型转换
double d = i;
printf("%d, %.2f\n" , i, d);
int* p = &i;
// 显示的强制类型转换
int address = (int) p;
printf("%x, %d\n" , p, address);
} 

缺点:转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换

 为什么C++需要四种类型转换

C风格的转换格式很简单,但是有不少缺点的:
1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
2. 显式类型转换将所有情况混合在一起,代码不够清晰
因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格

标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
static_cast、reinterpret_cast、const_cast、dynamic_cast

static_cast

static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用

注意:static_cast,但它不能用于两个不相关的类型进行转换

static_cast 主要用于以下几种情况:

1. 类型转换:将一个类型转换为另一个相关的类型。

2. 修正符号:将无符号类型转换为有符号类型,或者将有符号类型转换为无符号类型。

3. 避免隐式类型转换:在一些情况下,C++会执行隐式的类型转换,而 static_cast 可以明确地指定类型转换的行为,提高代码的可读性和可维护性。

以下是 static_cast 的基本语法:

result = static_cast<new_type>(expression);
result 是转换后的结果
new_type 是要转换成的新类型
expression 是要转换的表达式。

例如,将一个 int 类型转换为 double 类型:

int x = 10;
double y = static_cast<double>(x);
reinterpret_cast

使用 reinterpret_cast 进行类型转换时,编译器会强制执行转换,而不进行任何类型检查。这使得 reinterpret_cast 是最不安全的类型转换之一,因为它允许将任何指针类型转换为任何其他指针类型,即使它们在语义上没有关联。

reinterpret_cast 主要用于以下情况:

1. 将一个指针转换为另一个不同类型的指针,通常用于在底层进行类型转换,例如将 void* 指针转换为其他类型的指针。

2. 将一个指针转换为一个整数类型,或将一个整数类型转换为一个指针。这种转换通常用于处理指针地址的表示或存储。

以下是 reinterpret_cast 的基本语法:

result = reinterpret_cast<new_type>(expression);

result 是转换后的结果
new_type 是要转换成的新类型
expression 是要转换的表达式

事例

double d = 12.34;
int a = static_cast<int>(d);
cout << a << endl;

// 这里使用static_cast会报错,应该使用reinterpret_cast
//int *p = static_cast<int*>(a);
int *p = reinterpret_cast<int*>(a);
const_cast

使用 const_cast 进行类型转换时,编译器会修改表达式的常量性,但不会修改其值。这意味着通过 const_cast 转换后的指针或引用可以修改对象的值,即使原始对象被声明为常量也是如此。

const_cast 主要用于以下情况:

1. 去除指向常量对象的指针或引用的常量性,以便修改对象的值。

2. 添加指向非常量对象的指针或引用的常量性,以防止修改对象的值。

以下是 const_cast 的基本语法:

result = const_cast<new_type>(expression);

result 是转换后的结果
new_type 是要转换成的新类型
expression 是要转换的表达式

事例

const int a=2;
int* p=const_cast<int*>(&a);
    
(*p)++;

//a++;//这里的a仍然不能修改,const int类型
    
cout<<a<<endl;
cout<<*p<<endl;
    
cout<<&a<<endl;
cout<<p<<endl;

结果:
2
3
0x7ffeefbff3f8
0x7ffeefbff3f8

为什么*p和a的值不一样呢?这是编译器做的优化
如果将a定义为volatile const int a=2;

结果:
3
3
0x7ffeefbff3f8//&a的值这里大家和我的可能不一样,我在输出a的地址时,加了强制转换cout<<(int*)&a<<endl,我之所以强制转换,因为cout的匹配出了问题
0x7ffeefbff3f8
dynamic_cast

dynamic_cast 只能用于具有虚函数的类(即多态类),并且在运行时进行类型检查。它会检查所请求的类型是否与对象的实际类型兼容,如果兼容,则执行转换,否则返回空指针(对指针进行转换时)或抛出 std::bad_cast 异常(对引用进行转换时)。

dynamic_cast 主要用于以下情况:

1. 将指向基类的指针或引用转换为指向派生类的指针或引用。

2. 在运行时进行类型安全的向下转换,并且需要检查转换是否有效。

以下是 dynamic_cast 的基本语法:

result = dynamic_cast<new_type>(expression);

result 是转换后的结果
new_type 是要转换成的新类型
expression 是要转换的表达式

事例

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;
}

 🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸 

  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值