【C++入门到精通】特殊类的设计 |只能在堆 ( 栈 ) 上创建对象的类 |禁止拷贝和继承的类 [ C++入门 ]

在这里插入图片描述

阅读导航

  • 引言
  • 一、特殊类 --- 不能被拷贝的类
    • 1. C++98方式:
    • 2. C++11方式:
  • 二、特殊类 --- 只能在堆上创建对象的类
  • 三、特殊类 --- 只能在栈上创建对象的类
  • 四、特殊类 --- 不能被继承的类
    • 1. C++98方式
    • 2. C++11方法
  • 总结
  • 温馨提示

引言

在面向对象编程中,特殊类是指具有不同于常规类的特殊属性或限制的类。这些类可以通过各种方式达到特定的目标和需求,例如只能在堆 ( 栈 ) 上创建对象的类、禁止拷贝和继承等。

本文将会讨论四种常见的特殊类:不能被拷贝的类、只能在堆上创建对象的类、只能在栈上创建对象的类以及不能被继承的类。我们将介绍它们的实现方法和应用场景,并提供相应的代码示例以帮助读者更好地理解这些特殊类的概念和用法。

一、特殊类 — 不能被拷贝的类

当一个类的拷贝是不允许的,可以采取以下两种方式来实现:

1. C++98方式:

在C++98中,可以通过将拷贝构造函数和赋值运算符重载声明为私有,并且不实现它们来禁止拷贝。这样做的原因是私有访问权限限制了类外部的代码无法访问这两个函数的实现

class CopyBan {
private:
    CopyBan(const CopyBan&); // 声明拷贝构造函数为私有
    CopyBan& operator=(const CopyBan&); // 声明赋值运算符重载为私有
public:
    // 其他公共成员函数和数据成员
};

这种方式通过将拷贝构造函数和赋值运算符重载放在私有区域,从而阻止了类的外部对象调用这两个函数,实现了禁止拷贝的目的。

2. C++11方式:

在C++11中,可以使用delete关键字来删除拷贝构造函数和赋值运算符重载。通过在函数声明后加上= delete,可以明确告诉编译器要删除此函数,防止类外部的代码调用它

class CopyBan {
public:
    CopyBan(const CopyBan&) = delete; // 删除拷贝构造函数
    CopyBan& operator=(const CopyBan&) = delete; // 删除赋值运算符重载
    // 其他公共成员函数和数据成员
};

这种方式更加简洁明了,直观地表达了禁止拷贝的意图。使用= delete语法可以方便地阻止拷贝构造函数和赋值运算符重载的调用。

无论是C++98还是C++11,这两种方式都能有效地禁止类的拷贝,增强代码的稳定性和安全性。选择哪种方式取决于你使用的C++版本和个人偏好。

二、特殊类 — 只能在堆上创建对象的类

要设计一个只能在堆上创建对象的类,可以使用私有的析构函数和静态成员函数来实现:

class HeapOnly {
private:
    HeapOnly() {} // 私有的默认构造函数,防止在栈上创建对象
    ~HeapOnly() {} // 私有的析构函数,防止在栈上销毁对象

public:
    static HeapOnly* createInstance() {
        return new HeapOnly();
    }

    void destroyInstance() {
        delete this;
    }
};

这个类中,私有的默认构造函数和析构函数阻止了在栈上创建和销毁对象。而通过静态的createInstance()函数,可以在堆上创建该类的对象,并返回指向该对象的指针。然后,我们可以使用对象的destroyInstance()函数在适当的时候手动删除对象,从而释放内存

以下是示例代码的使用方式:

int main() {
    HeapOnly* obj = HeapOnly::createInstance();

    // 使用对象

    obj->destroyInstance();

    return 0;
}

🚨🚨注意:在这种设计中,由于析构函数是私有的,不能直接使用delete操作符来销毁对象。只能通过调用对象的destroyInstance()函数来手动删除对象

三、特殊类 — 只能在栈上创建对象的类

要设计一个只能在栈上创建对象的类,可以使用私有的构造函数和公有的静态成员函数来实现:

class StackOnly {
public:
    static StackOnly createInstance() {
        return StackOnly{};
    }

private:
    StackOnly() = default; // 私有的默认构造函数,防止在堆上创建对象
    ~StackOnly() = default; // 默认析构函数
};

这个类中,私有的构造函数阻止了在堆上创建对象。而通过公有的静态createInstance()函数,可以在栈上创建该类的对象,并返回该对象的副本由于没有指针和动态内存分配,对象的生命周期由对象所在的作用域控制,当对象离开作用域时,会自动调用析构函数销毁对象

以下是示例代码的使用方式:

int main() {
    StackOnly obj = StackOnly::createInstance();

    // 使用对象

    return 0;
}

这样设计的类限制了对象只能在栈上创建,可以避免使用者误用动态内存分配,从而提高了代码的健壮性。但请注意,在这种设计中,对象的拷贝构造函数、赋值运算符重载函数需要适当处理,以确保对象的正确复制行为。

四、特殊类 — 不能被继承的类

1. C++98方式

// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:
	static NonInherit GetInstance()
	{
		return NonInherit();
	}
private:
	NonInherit()
	{}
};

🚨🚨注意:在C++98中,虽然可以将构造函数私有化以阻止派生类调用基类的构造函数,但这并不能完全阻止继承。派生类仍然可以继承基类的成员函数和非私有成员变量

2. C++11方法

要设计一个不能被继承的类,可以使用C++中的关键字final来实现:

class NonInheritable final {
    // 类的定义
};

通过在类的声明中添加final关键字,可以阻止其他类继承该类。这样设计的类将是最终类,不能被其他类所继承。

以下是示例代码的使用方式:

class Derived : public NonInheritable { // 编译错误,无法继承NonInheritable类

};

int main() {
    NonInheritable obj; // 创建NonInheritable类的对象

    // 使用对象

    return 0;
}

这样设计的类不能被继承,可以确保类的封装性和稳定性,防止其他类对其进行修改或破坏。这在某些情况下非常有用,特别是当你希望限制类的继承性时。

总结

这些特殊类在面向对象编程中有着重要的作用,并且通过选择合适的类设计模式,我们可以更好地满足需求和解决问题。

在下一篇文章中,我们将深入研究单例模式。单例模式是一种常用的设计模式,它确保一个类只有一个实例,并提供了全局访问点。当我们需要确保只有一个对象来协调系统操作或管理共享资源时,单例模式非常有用。通过深入学习单例模式,我们将能够更好地理解其原理和使用方法,以及如何在实际开发中应用它。单例模式是一种非常有用的设计模式,掌握它将有助于我们编写可靠、高效的代码。让我们一起探索单例模式的精髓吧!敬请期待下一篇文章的发布!

温馨提示

感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/352711.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nginx与keepalived实现集群

提醒一下:下面实例讲解是在mac虚拟机里的Ubuntu系统演示的; Nginx与keepalived实现集群实现的效果 两台服务器都安装Nginx与keepalived: master服务器的ip(192.168.200.2) backup服务器的ip(192.168.200.4) 将 master服务器Nginx与keepalive…

【Mybatis的一二级缓存】

缓存是什么? 缓存其实就是存储在内存中的临时数据,这里的数据量会比较小,一般来说,服务器的内存也是有限的,不可能将所有的数据都放到服务器的内存里面,所以, 只会把关键数据放到缓存中&#x…

C# 命名管道NamedPipeServerStream使用

NamedPipeServerStream 是 .NET Framework 和 .NET Core 中提供的一个类,用于创建和操作命名管道的服务器端。命名管道是一种在同一台计算机上或不同计算机之间进行进程间通信的机制。 命名管道允许两个或多个进程通过共享的管道进行通信。其中一个进程充当服务器&…

RNN预测下一句文本简单示例

根据句子前半句的内容推理出后半部分的内容,这样的任务可以使用循环的方式来实现。 RNN(Recurrent Neural Network,循环神经网络)是一种用于处理序列数据的强大神经网络模型。与传统的前馈神经网络不同,RNN能够通过其…

独享http代理安全性是更高的吗?

不同于共享代理,独享代理IP为单一用户提供专用的IP,带来了一系列需要考虑的问题。今天我们就一起来看看独享代理IP的优势,到底在哪里。 我们得先来看看什么是代理IP。简单来说,代理服务器充当客户机和互联网之间的中间人。当你使用…

C/C++ - 面向对象编程

面向对象 面向过程编程: 数据和函数分离:在C语言中,数据和函数是分开定义和操作的。数据是通过全局变量或传递给函数的参数来传递的,函数则独立于数据。函数为主导:C语言以函数为主导,程序的执行流程由函数…

复式记账的概念特点和记账规则

目录 一. 复式记账法二. 借贷记账法三. 借贷记账法的记账规则四. 复试记账法应用举例4.1 三栏式账户举例4.2 T型账户记录举例4.3 记账规则验证举例 \quad 一. 复式记账法 \quad 复式记账法是指对于任何一笔经济业务都要用相等的金额,在两个或两个以上的有关账户中进…

GIT使用,看它就够了

一、目的 Git的熟练使用是一个加分项,本文将对常用的Git命令做一个基本介绍,看了本篇文章,再也不会因为不会使用git而被嘲笑了。 二、设置与配置 在第一次调用Git到日常的微调和参考,用得最多的就是config和help命令。 2.1 gi…

4核16G幻兽帕鲁服务器性能测评,真牛

腾讯云幻兽帕鲁服务器4核16G14M配置,14M公网带宽,限制2500GB月流量,系统盘为220GB SSD盘,优惠价格66元1个月,277元3个月,支持4到8个玩家畅玩,地域可选择上海/北京/成都/南京/广州,腾…

第十六章 Spring cloud stream应用

文章目录 前言1、stream设计思想2、编码常用的注解3、编码步骤3.1、添加依赖3.2、修改配置文件3.3、生产3.4、消费3.5、延迟队列3.5.1、修改配置文件3.5.2、生产端3.5.2、消息确认机制 消费端 前言 https://github.com/spring-cloud/spring-cloud-stream-binder-rabbit 官方定…

GPT-SoVITS 本地搭建踩坑

GPT-SoVITS 本地搭建踩坑 前言搭建下载解压VSCode打开安装依赖包修改内容1.重新安装版本2.修改文件内容 运行总结 前言 传言GPT-SoVITS作为当前与BertVits2.3并列的TTS大模型,于是本地搭了一个,简单说一下坑。 搭建 下载 到GitHub点击此处下载 http…

【网站项目】基于SSM的246品牌手机销售信息系统

🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板&#xff…

蓝桥杯——每日一练(简单题)

题目 给定n个十六进制正整数,输出它们对应的八进制数。 解析 一、通过input()函数获得需要转化的数字个数 二、for循环的到数字 三、for循环先将16进制转化为10进制,再输出8进制 代码 运行结果

直线拟合(支持任意维空间的直线拟合,附代码)

文章目录 一、问题描述二、推导步骤三、 M A T L A B MATLAB MATLAB代码 一、问题描述 给定一系列的三维空间点 ( x i , y i , z i ) , i 1 , 2 , . . . , n (x_i,y_i,z_i),i1,2,...,n (xi​,yi​,zi​),i1,2,...,n,拟合得到直线的方程。本文的直线拟合方法适用于任…

如何用一根网线和51单片机做简单门禁[带破解器]

仓库:https://github.com/MartinxMax/Simple_Door 支持原创是您给我的最大动力… 原理 -基础设备代码程序- -Arduino爆破器程序 or 51爆破器程序- 任意选一个都可以用… —Arduino带TFT屏幕——— —51带LCD1602——— 基础设备的最大密码长度是0x7F,因为有一位…

10.Golang中的map

目录 概述map实践map声明代码 map使用代码 结束 概述 map实践 map声明 代码 package mainimport ("fmt" )func main() {// 声明方式1var map1 map[string]stringif map1 nil {fmt.Println("map1为空")}// 没有分配空间,是不能使用的// map…

Vulnhub-dc6

信息收集 # nmap -sn 192.168.1.0/24 -oN live.port Starting Nmap 7.94 ( https://nmap.org ) at 2024-01-25 14:39 CST Nmap scan report for 192.168.1.1 Host is up (0.00075s latency). MAC Address: 00:50:56:C0:00:08 (VMware) Nmap scan report for 192.168.1.2…

IS-IS:10 ISIS路由渗透

ISIS的非骨干区域,无明细路由,容易导致次优路径问题。可以引入明细路由。 在IS-IS 网络中,所有的 level-2 和 level-1-2 路由器构成了一个连续的骨干区域。 level-1区域必须且只能与骨干区域相连,不同 level-1 区域之间不能直接…

.NET高级面试指南专题一【委托和事件】

在C#中,委托(Delegate)和事件(Event)是两个重要的概念,它们通常用于实现事件驱动编程和回调机制。 委托定义: 委托是一个类,它定义了方法的类型,使得可以将方法当作另一个…

SpringMVC第六天(拦截器)

概念 拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行 作用: 在指定的方法调用前后执行预先设定的代码 阻止原始方法的执行 拦截器与过滤器的区别 归属不同:Filter属于Servlet技术,I…