类和对象(中)

目录

1.类的6个默认成员函数

 2. 构造函数

3.析构函数

4.拷贝构造

五、赋值运算符重载

1.运算符重载

2.赋值运算符重载

知识补充


1.类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员
函数。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

 2. 构造函数

         构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,构造函数的任务是初始化对象。并且在对象整个生命周期内只调用一次。

其有四大特征

1. 函数名与类名相同。
2. 无返回值。
3. 对象实例化时编译器自动调用对应的构造函数。
4. 构造函数可以重载。

class Date
{
 public:
   // 1.无参构造函数
   Date()
  {
    _year = 1;
    _month = 1;
    _day = 1;
   }
   // 2.带参构造函数
   Date(int year, int month, int day)
  {
     _year = year;
     _month = month;
     _day = day;
  }
 private:
   int _year;
   int _month;
   int _day;
};
int main()
{

Date d1;
Date d2(2024,7,13);
return 0;
}

注意,如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明 

同时我们还可以运用之前在c++入门中缺省值的知识来进行一些优化。我们构造一个全缺省的构造函数

 Date(int year=1, int month=1, int day=1)
  {
     _year = year;
     _month = month;
     _day = day;
  }

因此我们可以构造

Date d1;
Date d2(2024);
Date d3(2024,7);
Date d4(2024,7,13);

注意,全缺省的函数与无参的函数,在语法上构成函数重载,但是在实际调用时会产生错误,因为编译器不知道调用哪一个

关于系统自动生成的构造函数

1.我们不写才会生成,我们写了任意一个构造函数就不会生成了

2.不会处理内置类型的成员,例如int double...,是随机值,所有指针,包括自定义类型的指针也都是内置类型,一般会初始化为nullptr。(但是c++11支持声明的时候给缺省值)

这个缺省值是给自动生成的构造函数使用的,但是当我们自己写了构造函数但是没有初始化那个内置类型时,也会使用缺省值

3.会处理自定义类型的成员,会去调用这个成员的默认构造函数

我们用栈来实现队列,不需要自己写队列的构造函数,自动生成的构造函数会帮我们调栈的构造函数 

总结:一般情况下都需要我们去写构造函数,除非成员全都是自定义函数 。

注意点:无参的构造函数,全缺省的构造函数,编译器默认生成的构造函数都被称为默认构造函数(不传参就可以直接用)。他们三个只能存在一个,多个并存会出现二义性。

3.析构函数

是与构造函数相配套的函数,对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

析构函数也有四大特征

1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值类型。
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构
函数不能重载
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数

和构造函数类似,析构函数不会处理内置类型成员

但是对于自定义类型,会调用这个成员的析构函数

后定义的函数会先调用析构函数

小总结:c++构造函数和析构函数最重要的特点是自动调用。

4.拷贝构造

我们先由一个现象引出拷贝构造

在c语言中(Stack已经使用typedef)

        我们_array是通过malloc开辟空间的,这时候我们发现,传参的时候s仅仅只是将s1中_array变量中储存的地址拿了过去,只是将main栈帧里面存的东西原模原样复制到func2栈帧中而已,他们的_array中存的都是指向堆区的同一块空间的指针。这是一种浅拷贝

        但是如果我们在c++中这样使用,当我们调用结束,系统编译器自动调用析构函数时,同一块空间就会出现被释放两次的情况,就会出现错误。

为了避免这种状况,c++中传值传参要调用拷贝构造。

拷贝构造函数的定义

拷贝构造函数:只有单个形参,该形参是对相同类型对象的引用(一般常用const修饰,例如Date(const Date &d) ),在用于已存
在的类型对象创建新对象时由编译器自动调用。

拷贝构造的特征

1. 拷贝构造函数是构造函数的一个重载形式

2. 拷贝构造函数的参数只有一个且必须是同类型对象的引用,使用传值方式编译器直接报错,
因为会引发无穷递归调用。

如果是传值,如下图

我们首先要传值传参,把d1传到d,就要调用我们自己的拷贝构造函数,把d1拷贝到d

然后我们这个拷贝构造函数又要传值传参,把d1传给d,就又要调用我们自己的拷贝构造函数

因此进行循环

3. 若未显式定义,编译器会生成默认的拷贝构造函数,它对于内置类型进行浅拷贝,对于自定义类型调用它的拷贝构造。 因此当我们没有写任何一个拷贝构造函数时,传值传参时候的现象于c语言中一模一样。这也是c++兼容c语言的体现

小知识

1.深拷贝举例

2. 拷贝构造另一等价写法

Date d3 = d1

与Date d3(d1)是等价的

3.传引用传参是不需要拷贝构造的

4.传值返回创建临时变量时,也会调用拷贝构造

(临时变量sizeof较小的话会用寄存器返回,如果较大,会在main函数中提前开辟空间)

(并且临时变量具有常性)

五、赋值运算符重载

1.运算符重载

运算符重载之后会使我们的代码可读性更强

 函数名字为:关键字operator后面接需要重载的运算符符号。例如:operator >  ,operator +

函数原型:返回值类型 operator操作符(参数列表) 例如

bool operator==(const Date& d1, const Date& d2)

注意点

1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个自定义类型参数,用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
3.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
藏的this

例如  bool operator==(const Date& d)        d1.opetator ==(d2)

当然也可隐式 d1 == d2  二者等价


4.   .*   ::   sizeof   ?: (三目操作符)     . 注意以上5个运算符不能重载。这个经常在笔试选择题中出

2.赋值运算符重载

仍然用日期类进行举例

void operator= (const Date& d)//这里const防止被误修改 不传引用也可以,但是传引用不用再开空间,效 
                               //率更高
{
 _year = d._year;
 _month = d._month;
 _day = d._day;
}

使用时

d1.operator=(d2);
d1 = d2;

与拷贝构造有点像,但是与拷贝构造不同的是,我们要现有两个对象,然后把一个对象的值赋到另一个对象中

拷贝对象是依靠一个已经存在的对象初始化另一个对象

不过我们还可以继续优化

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

return *this;
}

这样就可以支持连续赋值了

d1 = d2 = d3;

而且一般出了作用域还在,我们就返回引用。

为了防止有人写

d1 = d1;

这样的代码我们可以添加一个if判断一下 

Date& operator= (const Date& d)
{
if(this != &d)
{
     _year = d._year;
     _month = d._month;
     _day = d._day;
}
return *this;
}

再小小地补充一个知识

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

return *this;
}

传值传参和传值返回的时候都要调用拷贝构造,一个是把实参传给形参,另一个是创建一个临时变量 。

最后,对于赋值重载,我们不写它也会自动生成,它对于内置类型会进行浅拷贝,对于自定义类型会调用它的赋值重载函数

知识补充

函数如果声明和定义分开,缺省值只能在声明的时候给。声明和定义分开可以让我们更容易地去看这个类的结构。

声明的时候给了缺省值,编译的时候就可以编译通过了,然后在链接的时候通过修饰过后的函数名去找这个函数

在.cpp中定义函数的时候,函数名前面要加类名:

相关推荐

  1. C++对象()

    2024-07-15 05:14:04       63 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-15 05:14:04       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-15 05:14:04       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-15 05:14:04       58 阅读
  4. Python语言-面向对象

    2024-07-15 05:14:04       69 阅读

热门阅读

  1. C# Winform之propertyGrid控件使用详解和分组设置

    2024-07-15 05:14:04       26 阅读
  2. ComfyUl提示词技巧

    2024-07-15 05:14:04       29 阅读
  3. 基于智能算法的品牌视觉识别系统优化研究

    2024-07-15 05:14:04       23 阅读
  4. Windows图形界面(GUI)-DLG-C/C++ - 图形界面程序结构

    2024-07-15 05:14:04       29 阅读
  5. 自学黑客

    2024-07-15 05:14:04       21 阅读
  6. 归并排序(递归实现)

    2024-07-15 05:14:04       25 阅读
  7. SpinalHDL之VHDL 和 Verilog 生成

    2024-07-15 05:14:04       25 阅读
  8. 等保测评助力网络安全治理现代化

    2024-07-15 05:14:04       20 阅读
  9. 去中心化预言机是什么

    2024-07-15 05:14:04       32 阅读