当前位置: 首页 > backend >正文

c++类与对象(二)

前言: 我们之前学习了类是一个泛指的概念比较虚无,之后学习了对象,对象是类的具体实例,如今我们要进一步掌握,我们如何运用类与对象去解决问题,以及它的思考模式。但是为了更好的解决一些我们对一个对象的基本的操作:赋初始值,销毁对象,简便的使用基本操作符实现对象之间的运算符操作, 取地址运算符重载(不常用),我们的对象都会有这些默认的成员函数也就是说已经帮你实现了一个对象一创建这些家伙就有了,其中有一些甚至会被自动调用,但是它可能达不到你的预期所以需要你去具体实现它。

        对应功能的六大默认的成员函数如下:

长相如Date类的头文件展示:

一.构造函数 

     构造函数是默认成员函数之一,1.函数名与类名相同 2. 无返回值 3. 可以写多个构造函数构成重载 3. 对象实例化会自动调用(不信的调试试试)

    创建对象的时候,写构造函数 可以进行赋初始值,写法如下:

下面几种是等价的:

  主函数中:创建对象:

   Date d1 = Date(1999,1,1);

   Date d1(1999,1,1);

   // but! 注意哈 人家这个它可没有开辟空间哈这只不过就是一个静态的在栈上 以及呢 调用了构造成员函数 进行赋初值
  

      注意如下:切记哈: 对应创建的无参构造函数他会自动调用 你只需要写类 +对象名就行会自动调用默认的无参构造函数。 不需要写成类 +对象名()不然编译器报错 认为

public:
    Date()
    {
        printf("haha");
    }

int main()

{

   Date d1();// 编译器报错 认为 这是在声明一个默认构造

}

Date类举例:

 class Date{public:// 1.无参构造函数Date(){}// 2.带参构造函数Date(int year, int month, int day){_year = year;5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦
用户显式定义编译器将不再生成。_month = month;_day = day;}private:int _year;int _month;int _day;};void TestDate(){Date d1; // 调用无参构造函数Date d2(2015, 1, 1); // 调用带参的构造函数// 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明// 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象// warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)Date d3();

     注意: 1.1默认构造函数 又名无参构造函数 所以你说哈 

Date(int year = 1900, int month = 1, int day = 1);
Date();
int main()
{Date d1;
}

会发生什么呢 会报错因为不知道调用哪个啊 全缺省也可以接受无参构造呢 。

总之 通过调用构造函数可以对对象进行赋初值。

1.2 在成员变量声明中给初值 

 class Date
 {
  private:
      int _year =1980;
      int _month = 1;
      int _day = 1;
 };

二.析构函数 

构造函数是默认成员函数之一 ,1.函数名与类名相同 2. 写法是函数名前面加一个~ 3.就写一个 3. 无返回值 ,参数4. 在该对象结束的时候会自动调用  

  特点:1对于内置数据

    作用用来清理资源空间: 比如1.清理动态内存,2.对一些指针进行赋空值 3.调用自定义类型(也就是你自己写的类)的析构函数   

 class Stack
 {

public:

   ~Date()

{

     free(a);

     a = nullptr;

     _size = _capacity = 0;

}


  private:
    int *_a;

     int  _capacity;

        int _size;
 };

     注意点:你认为下面代码输出啥?:

class Time {public:~Time(){cout<<"~Time()"<<endl;}};
class myDate {public:myDate(){cout << "myDate()"<<endl;}~myDate(){cout << "~myDate()"<<endl;}private:int _year;int _month;int _day;Time t1;};
int main()
{myDate d1;}

会输出: myDate()
~myDate()
~Time()

tip

 为啥: 明明我在Date类中没有调用time的析构啊 ,谁干的: c++生命周期的管理机制他会检测当一个对象的生命结束后会调用它的析构函数 完成它的资源清理。

2. 析构顺序

对于同一类的创建的对象析构的顺序会于它的创建顺序相反 这里用静态变量来证明:

    3. 常用情况

   对于设计到动态资源申请的尽量自己实现析构函数 推荐一道题目 : 232. 用栈实现队列 - 力扣(LeetCode)

三.拷贝构造函数

构造函数是默认成员函数之一,1.函数名与类名相同 2. 写法是函数名中是用const加引用的调用3.就写一个3.无返回值

   说白了就是进行赋值给对方 实现拷贝 

使用目的:  1. 它是为了对于一个已经创建了的对象拷贝给一个正在创建的对象 而不是 两个都是已经创建了的 类似于 int a = b;  而不是 c = a; 这样的

         1.1 如何使用 

举例:

Date(const Date& d)
{_year = d._year;_month = d._month;_day = d._day;
}

这是对于Date类的实现 :

 1.1.1 为什么必须是传递引用而不是 传值 

   传值长这样 Date(const Date d) ;  如果是传值调用成员函数的拷贝构造函数 。 如下 

int main()

{

Date d1(1990,1,1);

Date d2(d1); // 创建d2把d1拷贝给他 这是 这里是传值哈

}

   

接下来就会调用对象d2的拷贝构造函数但是在这之前又会拷贝一个临时对象因为是传值拷贝 给(const Date d) 这时为了创建d的拷贝 又会调用它的拷贝构造 就这样不停的调用拷贝构造 无线死循环了 :

所以 拷贝构造函数的参数只有一个那就是类对象的引用 

1.2 默认拷贝构造

   默认拷贝构造 会按照字节序的方式进行拷贝,这就是值拷贝,对于动态内存可能会出现问题 比如    一个栈中的stack s1 拷贝给stack s2     加入 s1 中的a的地址 是0x112233 在拷贝的时候 

会使得s2 中的a指向的地址也是0x112233 但是在调用析构的时候就会报错了:因为第一次析构 s1已经把0x112233这块内存给销毁了 但是 s2再次调用又会销毁一次 这就会报错。 

  所以设计到动态内存的对象一定要写拷贝构造函数 向日期类这样的对象都是int  没有设计的资源的申请 结束后内存就之间还给系统了,我们就可以不用写它的拷贝构造了。

2. 使用的常见以及编译器的优化 

   我们都已经了解到了,所谓拷贝构造就是将一个以及创建的拷贝给一个正在创建的对象。

   使用场景:  1.使用已存在对象创建新对象2.函数参数类型为类类型对象 3.函数返回值类型为类类型对象

 简单说 :创建对象 传参 返回值    

 如下哈:

我们的预期是输出四个打印 然后在析构掉 :但是实际上:

实际上这是因为设计到了:编译器的优化: 对于连续的拷贝构造和构造函数会进行优化直接拷贝构造   如上面的代码 第一次是构造: 地址6E8    然后调用Test(d1) 拷贝构造给d1 然后又拷贝构造给temp  这时候就只执行了一次 直接拷贝构造给temp (优化)    temp 地址 (7D8) 

   返回值的时候 把temp返回 这时候又进行拷贝构造返回 所以又得到一个返回值 地址 824 

  拓展:设计到的内存管理

    这里进行析构的时候 最先被销毁的是7D8也就是 temp的地址 我们之前学习了 不是说同一对象先创建的后销毁吗 那是要对于同一生命周期的情况下 ,temp是定义在栈里面的它一出栈就结束生命周期了 而对于一开始的d1 以及返回值 824 他们是在主函数结束才完成生命周期 我们根据先创建后销毁 所以应该是 824先被销毁。

四.赋值运算符重载函数

   1. 运算符重载 

      学习赋值运算符重载之前我们了解一下啥?是运算符重载:

C++ 为了增强代码的可读性引入了运算符重载 运算符重载是具有特殊函数名的函数 ,也具有其
返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字 operator 后面接需要重载的运算符符号
函数原型: 返回值类型  operator 操作符 ( 参数列表 )
不能通过连接其他符号来创建新的操作符:比如 operator@
重载操作符必须有一个类类型参数
用于内置类型的运算符,其含义不能改变,例如:内置的整型 + ,不 能改变其含义
作为类成员函数重载时,其形参看起来比操作数数目少 1 ,因为成员函数的第一个参数为隐
藏的 this
                       -------》             
         注意以上 5 个运算符不能重载。这个经常在笔试选择题中出现。
使用的时候 可以是 :  
if(d1>=d2)

 

类似的还要很多 +=  - 啊 等等 

2. 赋值运算符重载 

   首先赋值元素符重载 返回得到的是 对象的类型 其次 它是默认成员函数 你不显式写编译器会自动生成。

这里需要注意的就是 前置++ 和后置加加 奇怪都是 operator++() 怎么区分呢 :

// C++ 规定:后置 ++ 重载时多增加一个 int 类型的参数,但调用函数时该参数不用传递,编译器
自动传递 

五. 关于取地址运算符重载 

  其实一般这两个家伙不用自己写 : 一个就是 你得到它的地址 没有现在 甚至可以修改 另一个就是 你只能得到它的地址但是不能修改它的内容只能访问 。

1. 如何给this指针加上const呢 

   首先this指针是被隐式生成的 我们无法这样写 Date(const Date*this) 就算这样写了也会报错 因为编译器会隐式生成了 所以 祖师爷想了一招 把const写到外面去 意思不变 也就让人不能修改this指定的内容  Date()const   所以所以的成员函数如果想加const修饰就去往外面加const 

2. 取地址运算符重载 

     日期类举例:

class Date
{ 
public :Date* operator&(){return this ;//这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需
//要重载,比如想让别人获取到指定的内容!}const Date* operator&()const{return this ;}
private :int _year ; // 年int _month ; // 月int _day ; // 日
};

到这里默认成员函数就被如此优秀的你复习完了,那我问你,你要不自己写一个日期类呢! 

  特别是里面的 日期-日期 日期+日期  我保证你会很难受 。;;;;;;;; 哈哈哈哈 别问我为什么知道     

http://www.xdnf.cn/news/8625.html

相关文章:

  • 您的浏览器不支持摄像头API—仙盟创梦IDE
  • 浅析Spring AOP 代理的生成机制
  • 为什么要使用线程池
  • 【概率论基本概念01】点估计
  • 《P3435 [POI 2006] OKR-Periods of Words》
  • 【Linux网络篇】:Socket网络套接字以及简单的UDP网络程序编写
  • 【Node.js】高级主题
  • 【Linux 学习计划】-- git 在Linux远端服务器上的部署与简单使用
  • LABVIEW 通过节点属性动态改变数值显示控件的方法
  • TypeScript入门到精通
  • 【Leetcode 每日一题】2942. 查找包含给定字符的单词
  • 机器学习算法-sklearn源起
  • 语音合成之十六 语音合成(TTS)跳跃与重复问题的解析:成因、机制及解决方案
  • Mac的显卡架构种类
  • 进程间通信I·匿名管道
  • 软考中级软件设计师全真题
  • Android中获取控件尺寸进阶方案
  • 【MySQL】06.内置函数
  • 机器学习第二十六讲:官方示例 → 跟着菜谱学做经典菜肴
  • spring boot 2.7集成旧的springfox-boot-starter swagger oas 3.0
  • 论文阅读笔记——Emerging Properties in Unified Multimodal Pretraining
  • 超全GPT-4o 风格提示词案例,持续更新中,附使用方式
  • 行为型:迭代器模式
  • java面试题
  • 物联网代理暴利逻辑拆解:格行随身WiFi三网切换技术实战分析
  • 机器学习中的多GPU训练模式
  • 向量数据库Milvus03-高级功能与性能调优
  • 7:QT加载保存参数(读写日志)
  • JS逆向 - 狗dong参数Log及joyytokem(补环境)
  • Groovy:Java 的简洁版