C++ 基础(十一)定义自己的数据类型
时间:2021-08-14
本文章向大家介绍C++ 基础(十一)定义自己的数据类型,主要包括C++ 基础(十一)定义自己的数据类型使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
1. 类和面向对象编程
- 类类型:组合基本类型和其他类型的结构。
- 面向对象编程的核心:封装,继承和多态。
- 封装:
- 对象的定义包含一组属性和一组函数。把这些数值和函数打包到一个对象中,就是封装。
- 数据隐藏:封装的意义在于数据隐藏,数据成员一般不希望被外部访问,对外的接口始终不发生太大的变化,内部的组成在变。
- 继承:
- 根据一个类定义另一个类。
- 派生类继承基类的成员,并可以定义自己的新成员。
- 派生类可以重写从基类继承的成员函数。
- 多态性:
- C++多态性总是涉及使用指针或者引用调用对象的成员函数。
- 这种特性仅适用于派生于公共类型的对象。属于一组继承性相关的类的对象,可以通过基类指针和引用来传递和操作。
- 派生类可以使用基类指针和引用传递,指针和引用可以调用它指向的对象继承的成员函数。
- 多态性体现是函数调用实际上会跟着类实际指向的对象而定,调用哪个函数是在运行时决定的。
2. 定义类
- 类成员默认是私有的。(struct 默认是公共的)。
- 使用public 和 private修饰符。
3. 构造函数
- 定义类的新实例时调用,创建新对象时初始化调用。
- 构造函数与类同名。
- 不定义构造函数时,编译器生成默认的构造函数。
- default关键字:
- 如果定义了一个有参构造函数,编译器就不再生成构造函数了,使用default关键字可以生成一个默认构造函数:Box() = default;
- 类外部定义函数和构造函数:
- 成员函数和构造函数都需要类名限定。
- 默认构造函数的参数值:
- 构造函数和成员函数默认值总放在类中,不放在外部构造函数和成员函数中。
- 成员初始化列表:
- 函数定义后,使用冒号分开。
- 需要按照成员函数初始化列表中的顺序进行初始化。
- explicit关键字:
- 只有一个参数的构造函数可能会出现问题。
- 这种类中有与类参数进行的操作时,该函数传递构造函数的参数,会发生隐式类型转换。
- 防止这种情况的做法是使用explicit关键字修饰构造函数。
- 编译器不会把explicit声明的构造函数用于隐式类型转换,只能在程序代码中显示创建对象,
- 委托构造函数:
- 类有多个构造函数,一个构造函数在初始化参数列表调用前一个构造函数,把构造工作交给另一个构造函数。
- 副本构造函数:
- 把类对象作为参数,编译器提供默认的副本构造函数。
- 副本构造函数的实参必须是const引用。Type::Type (const Type& object);
4. 访问私有成员
- 使用成员函数返回私有成员的值。通常定义在类内部,定义为内联函数。称为访问器函数,与之对应的有修改器函数。
5. this指针
- 执行成员函数时,都会包含一个隐藏的指针,称为this指针。
- 静态成员函数中不包含this指针。
- 返回this指针,函数返回值类型是该类的指针。优点是可以使用一个成员函数的返回值调用另一个成员函数。
Box* Box::setLength(double lv) { if(lv > 0) length = lv; return this; } Box* Box::setWidth(double wv) { if(wv > 0) width = wv; return this; } Box* Box::setHeight(double hv) { if(hv > 0) height = hv; return this; } Box myBox {3.0, 4.0, 5.0}; myBox.setLength(-20.0)->setWidth(40.0)->setHeight(10..0);
6. const 对象和const成员函数
- 综述:
- const修饰的对象成员变量都是const,都不能修改。
- const指针指向非const对象,也不能通过指针修改成员。
- 实参是非const,函数形参是const引用,不能在函数中修改对象内容。
- 通过const指针或者const引用,与指直接访问const对象有相同的限制。
- const成员函数
- 只使用const限定的对象没有任何用处,不能调用它的任何成员函数。
- 解决办法是在类中,不修改成员的函数使用const修饰。
double Box::volume() const { return length * length * length; }
- const 正确性
- 在const成员函数内部修改成员变量会失败。
- 把成员函数指定为const,该成员函数的this指针会也会成为cosnt指针。
- const成员函数不能调用非const成员函数。
- 重载const
- 由于const是函数签名的一部分,可以使用const版本重载一个非const版本的成员函数。
- 对于返回某个对象封装的内部数据的指针或引用的函数,常常进行重载。
double &length() { return _length; } // 重载为 const double & length()const { return _length; } // 但是double是基础类型,一般按值进行传递 double length() const { return _length; } // 对象是否为const决定了哪个成员函数会被调用 // 错误情况,开头没有const。 // 因为const成员函数隐含的this指针是const,类成员的名称是对const引用,所以返回类型是const引用 double & length() const { return _length; }
- 常量的强制转换
- 把const对象转为非const:
- const_cast<Type *> (expression); expression 是 const Type * 或者Type*
- const_cast<Type&> (expression); expression 是 const Type *、const Type &、Type或者Type*
- 但是不建议这样使用,可能会破坏对象的常量性。
- 把const对象转为非const:
- 使用mutable关键字
- 希望不修改const对象的特定成员,使用mutable关键字。
- 修饰的成员即使是const对象,该成员也可被修改。
- 典型用途是调试,记录日志,缓存或线程同步。
7. 友元
- 综述:
- 友元可以访问类对象的任意成员,无论这些成员的修饰符是什么。
- 友元声明破坏了面向对象编程的数据隐藏性。
- 运算符重载需要使用一些友元的场景。
- 友元函数
- 关键字friend,无法在类外将函数设置为类的友元。
- 类的友元函数可以是一个全局函数,也可以是另一个类的成员。
- 访问修饰符不能被用于类的友元函数。
- 友元函数一般都需要传入类对象作为参数,因为友元不是类成员,不能直接引用成员变量。
- 友元和一般函数一样,但可以不受限制地访问类中所有成员。
- 友元应该是别无选择时的选择。
- 友元类
- 把整个类声明为另一个类的友元。友元所有函数可以访问原有类的所有成员。
- 原有类不能访问友元类的私有成员。
- 友元关系不能传递。
- 嵌套类是类似场景下替代友元的一种选择。
8. 类的对象数组
- 一般都是无参构造。
- 类对象的大小,可以使用sizeof计算,存在边界对齐。
9. 类的静态成员:
-
综述:
- 类的成员可以声明为static。类的静态成员可以在类的范围内存储数据。
- 独立于对象,可以由对象访问,属于类属性。
- 静态成员函数独立于单个类对象,任何类对象都可以调用静态成员函数。
- 静态成员函数如果是公共成员,还能从类的外部调用。
- 静态成员函数的常见用法是无论是否声明了类对象,都能操作静态成员变量。
- 静态成员变量
- 关键字static,只定义一次。所有对象之间共享。
- 一个用途是计算个类的实例对象存在。
- C++17开始,支持内联变量。对静态成员初始化产生两种作用:inline声明时,可直接初始化;无inline时,在类外部初始化静态变量。相比之下,内联变量方便很多,可以在头文件中初始化。
- 类中定义了静态成员,对象的大小不改变,静态成员不是任何对象的一部分。
- 访问静态成员变量
- 通过对象访问或者通过类名限定符访问。
- 静态常量
- 静态成员往往用来定义常量。
- C++17之前静态常量的初始化工作复杂,C++17之后引入内联常量,初始化静态常量变得简单了。
- 规则,static和const的成员变量都声明为inline。
- 定义这类常量来包含函数参数的边界值。
- 类类型的静态成员变量
- 静态成员变量不是类对象的一部分,所以它可以与类具有相同的类型。即Box类包含一个Box类型的静态常量。
- 仍需要在类的外部进行初始化。const Box Box::refBox {10.0, 10.0, 10.0};
- 类的静态成员变量时在创建任何对象之前创建的。类对象的任何静态和非静态的成员都能访问refBox。
- 不能使用constexpr声明一个Box静态常量。C++11新标准规定,允许将变量声明为constexpr类型让编译器来验证变量的值是否是一个常量表达式。
- 静态成员函数
- 独立于类对象,没有类对象,也能调用公共静态成员函数。
- static关键字,使用对象调用或者使用类名限定符调用。
- 静态函数不能访问调用它的对象。为了让静态函数访问类的对象,需要把它作为实参传递给该函数。这种情况下,该函数可以访问对象的私有成员和公共成员。
- 静态成员函数时类的一个具有完全访问权限的成员。
10. 析构函数
- 释放类对象时,自动执行析构函数。
- 如果析构的函数体为空,最好加上default关键字。
- 需要明确调用析构的场景很少。
- 用户不定义析构函数时,编译器每次给每个类添加默认析构函数。
- 实际上编译器会默认添加的有:默认构造函数,默认副本构造函数,默认析构函数。
11. 使用指针作为类成员
- 确保技术删除所有对象,智能指针很有帮助。
- unique_ptr不会忘记对自由存储区分配的对象应用delete运算符。
- 多个对象指向并使用同一个对象时,且无法判断何时全部使用完时,std::shared_ptr<>非常有用。
- 应该使用智能指针来管理动态内存分配的对象,这种原则是RAII,资源获取即初始化。
- delete运算符收到nullptr时,不做任何动作,比较安全。
12. 嵌套类
- 嵌套类的名称被限定为包含类的作用域,并受到包含类的成员访问修饰符的影响。
- 不能在外部类外部创建,使用被内部类对象。
- 所有的Package成员都能在Truckload类内部直接访问,前提是内部类的成员都是public,不能在外部访问。
- 嵌套类 == 内部类 包含类 == 外部类
- 嵌套类的成员可以直接引用包含类的静态成员,类型或枚举类型。
- 包含类的其他成员访问嵌套类需要通过类对象,类对象的指针和引用。
- 嵌套类的成员函数访问外层类的成员时,与外层类成员函数权限相同。嵌套类成员函数可以访问外层类私有成员。
- public修饰符的嵌套类:
- Package定义放在Truckload公共部分。
- 可以在外部定义嵌套类的对象: Truckload::Package aPackage(Box);
- 迭代器模式:
- 问题由来:getxx()和getNextxx()允许遍历Truckload中所有的Box对象。遍历的机制是内部的一个指针。
SharedBox findLargestBox(const Truckload& truckload) { SharedBox largestBox { truckolad.getFirstBox() }; SharedBox nextBox { truckload.getNextBox() }; while(nextBox) { if(nextBox->compare(*largestBox) > 0) largestBox = nextBox; nextBox = truckload.getNextBox(); } return latgestBox; } // 存在的问题:getFirstBox() 和 getNextBox()都在更新内部的指针pCurrent,即它们必须是非const成员,但实参是引用const值的实参。 // 解决方法一是mutable,但存在缺陷,并发问题,会对同一集合使用嵌套循环。每个遍历都需要一个pCurrent指针。 // 解决方法二是为遍历Truckload而设计和创建另一个对象。这种对象叫做“迭代器”
-
- 使用迭代器的类定义(部分):
class Truckload { private: class Package { public: SharedBox pBox; Package* pNext; Package(SharedBox pb):pBox{pb}, pNext{nullptr} {} ~Package() { delete pNext; } }; Package* pHead {}; Package* pTail {}; public: class Iterator { private: Package* pHead; Package* pCurrent; friend class Truckload; explicit Iterator(Package* head) : pHead{head}, pCurrent{nullptr} {} public: SharedBox getFirstBox(); SharedBox getNextBox(); }; Iterator getIterator() const { return Iterator{phead}; } // other };
-
- 特点分析
- Iterator声明为Truckload的嵌套类,故具有与Truckload成员函数相同的访问权限。这样,可以访问私有成员Package类。
- Iterator必须是公共的,保证类外代码可以访问。
- Iterator的主构造是私有的,外部不能创建Iterator。
- 为了外层类能访问Iterator的私有构造,外部类声明为Iterator的友元。
- 特点分析
SharedBox findLargestBox(const Truckload& truckload) { auto iterator = truckload.getIterator(); SharedBox largestBox { iterator.getFirst(); }; SharedBox nextBox { iterator.getNextBox() }; while(nextBox) { if (nextBox->compare(*largestBox) > 0) largestBox = nextBox; nextBox = iterator.getNextBox(); } return largestBox; }
原文地址:https://www.cnblogs.com/yangdadahome/p/15115662.html
- Oracle 12c远程克隆PDB的问题及修复(r12笔记第78天)
- Oracle表中含有255列以上时需要注意的(r12笔记第77天)
- Golang语言--资源自动回收技术
- Oracle 12.2中的一个参数说明(r12笔记第76天)
- Golang语言社区--【游戏服务器知识】多线程并发
- 用100行Nodejs代码写微博爬虫
- MySQL无法创建表的问题分析(r12笔记第73天)
- Golang语言社区--【H5游戏开发基础知识】JavaScript 用法
- Oracle中的PGA监控报警分析二(r12笔记第87天)
- Oracle 12c PDB的数据备份恢复(r12笔记第84天)
- MySQL和Oracle中唯一性索引的差别(r12笔记第83天)
- 如何用JavaScript进行数组去重
- Oracle 12.1升级到12.2的两种方法(r12笔记第92天)
- Oracle数据库重启后密码失效的问题(r12笔记第91天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- K8s集群上使用Helm部署2.4.6版本Rancher集群
- VMware下安装CentOS
- leetcode多线程之按序打印
- 表格的实现
- 使用 Node.js 定制你的技术雷达:中篇
- 数据库PostrageSQL-在Windows上从源代码安装
- Redis的过期策略和内存淘汰策略及LRU算法详解
- 群晖Docker安装GitLab及腾讯企业邮件配置踩坑记录
- 基于docker搭建DNSmasq
- Django-admin配置和显示图标
- redis学习(八)
- 【剑指Offer】打印从1到最大的n位数
- 面试题-List之ArrayList、Vector、SynchronizedList、CopyOnWriteArrayList
- 面试题-JAVA设计模式之单例模式的5种实现方式
- 面试题-JAVA中的深拷贝、浅拷贝原理及实现