2024年最新类和对象(中)----第一部分_自动调用的只能是默认构造函数吗(2),大牛带你直击优秀开源框架灵魂

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

//默认对自定义类型调用其构造函数进行初始化

}

7. **关于编译器生成的默认成员函数,很多人会有疑惑:在我们不实现构造函数的情况下,编译器会生成 默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象\_year/\_month/\_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??**

 解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语法已经定义好的类型:如 int/char…,自定义类型就是我们使用class/struct/union自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员\_t调用的它的默认成员函数

 

class Time
{
public:
Time()//Time构造函数
{
_hour = 0;
_minute = 0;
_second = 0;
}
int _hour;
int _minute;
int _second;
};
class Date
{
public:
//当我们不写构造函数的时候,编译器会生成一个默认构造函数
//内置类型/基本类型:int/char/double/指针
//自定义类型:class/struct
//默认生成构造函数对于内置类型成员变量不会做处理,但是对于自定义类型的成员变量就会调用它们的构造函数进行初始化构造
//在下面的这个例子中,因为Date类型的变量中有自定义类型的成员变量_time,所以会调用_time即Time类型的构造函数,然后将_hour、_minute、_second初始化为0
//_year、_month、_day都是内置类型,所以不做处理,打印出来是随机数
void Print()
{
cout << _year << “-” << _month << “-” << _day << endl;
cout << _time._hour << ‘-’ << _time._minute << “-” << _time._second << endl;
}
private:
int _year;
int _month;
int _day;
Time _time;
};
int main()
{
Date d1;

d1.Print();

return 0;

}

 经过打印发现输出结果为\_year、\_month、\_day为随机数,而\_time.\_hour、\_time.\_minute、\_time.\_second为0。

 打印截图:

 ![image-20220516132729687](https://img-blog.csdnimg.cn/img_convert/7377d5ea3f930047e495c6f1e49d3880.png)

 总结:一般情况下一个C++类,都要自己写构造函数。一般只有少数情况可以让编译器默认生成。  
 1、类里面都是自定义类型成员,并且这些成员都提供了默认构造函数。  
 2、如果还有内置类型成员,声明时给了缺省值,也可以使用默认构造函数,当然这种情况相对来说比较少。  
 3、使用C++库里的STL数据结构时可以使用编译器自动生成的默认构造函数。

 注意:构造函数必须定义在public权限中,否则我们无法在主函数中定义相关的变量,因为如果我们将其放在private权限中,我们在类外是无法访问的,自然也就无法进行定义该种类型的变量。

 注意:看下面的例子:

 

class Time
{
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date()
{
_year = 0;
_month = 0;
_day = 0;
_time._hour = 0;
_time.minute = 0;
_time.second = 0;
}
void Print()
{
cout << _year << “-” << _month << “-” << _day << endl;
cout << _time._hour << ‘-’ << _time._minute << “-” << _time._second << endl;
}
private:
int _year;
int _month;
int _day;
Time _time;
};

 在上面的例子中,我们试图在Date的构造函数中将\_time.\_hour、\_time.\_minute、\_time.\_second进行初始化,但这是无法实现的,因为在Time类中,我们将这三个变量定义为私有权限,在类外无法进行访问,所以自然也就不能在Date的构造函数中进行初始化。

 结论:各个类中应当有自己的构造函数来初始化自己的成员变量,而不应该越俎代庖,即使另一个类中定义的成员变量的权限是public,我们也不应该如此去做,因为我们无法保证我们通常使用的类类型的成员变量是public,而且一般来说,成员变量的权限也都是private,这是一个好习惯。
8. 在上面的第6点中,默认构造函数有3种,需要注意的是,无论是三种中的哪一种默认构造函数,都能够对类中定义的自定义类型种的构造函数进行调用,例如下面的例子:

 

class Time
{
public:
Time()//会自动进行调用
{
_hour = 0;
_minute = 0;
_second = 0;
}
void Print()
{
cout << _hour << “-” << _minute << “-” << _second << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date()//在Date类的构造函数中,我们没有对Time构造函数进行显式调用(当然我们本身也无法进行显式调用),但实际上编译器会默认调用Time的构造函数的
{
_year = 0;
_month = 0;
_day = 0;
}
void Print()
{
cout << _year << “-” << _month << “-” << _day << endl;
_time.Print();
}
private:
int _year;
int _month;
int _day;
Time _time;
};
int main()
{
Date d1;

d1.Print();

return 0;

}

 打印结果:

 ![image-20220516142156636](https://img-blog.csdnimg.cn/img_convert/eb5cffb60de428ff344439f8617d48e6.png)
9. 注意看下面这段代码:

 

class Time
{
public:
Time(int hour)//自己定义的构造函数,有形参,且不是全缺省,所以不再属于默认构造函数了,所以无法被自动调用
{
_hour = 0;
_minute = 0;
_second = 0;
}
void Print()
{
cout << _hour << “-” << _minute << “-” << _second << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
//没有定义默认构造函数,所以编译器生成默认的构造函数,对内置类型赋随机值,对自定义类型调用其默认构造函数进行初始化
void Print()
{
cout << _year << “-” << _month << “-” << _day << endl;
_time.Print();
}
private:
int _year;
int _month;
int _day;
Time _time;
};
int main()
{
Date d1;
return 0;
}

 此时程序会报错,因为Date类型中的Time类型的变量没有默认构造函数(即我们自己手动定义的构造函数把编译器默认生成的构造函数给掩盖了),所以程序无法通过。那么这种情况下有没有解决方案呢?答案是有的,解决方案就是我们需要初始化列表,后面我们会学到。
10. **成员变量的命名风格:我们一般习惯在成员变量的前面加一个`_`,或者在后面加也可以,或者在成员变量的前面加一个m(member的意思)**。这是为了防止下面情况的出现:

 

class Date
{
public:
Date(int year, int month, int day)
{
year = year;//注意:此时的year是形参,而不是成员变量,此时符合就近原则,哪个近是哪个,不是成员对象
month = month;
day = day;
}
private:
int year;
int month;
int day;
Time _time;
};

 **此时编译器将无法区分year是形参还是Date类中的成员变量了。**

 当然,我们可以像下面这样定义构造函数来避免上面存在的问题:

 

class Date
{
public:
Date(int year, int month, int day)
{
this -> year = year;
this -> month = month;
this -> day = day;
}
private:
int year;
int month;
int day;
Time _time;
};

 虽然上面的这种方法能够很好的解决问题,但是我们还是推荐在成员变量的前面或后面加上`_`的方式或者在前面加上一个m的方式来避免上面存在的这种问题。


### C++11中有关默认构造函数的补丁


我们知道C++的默认构造函数会对内置类型初始化为随机值,这常常会使我们无法或者很难真正使用到默认构造函数,所以C++11中出现了相关的补丁:在成员变量(内置类型)的定义时进行初始化。



class Date
{
private:
int _year = 0;
int _month = 0;
int _day = 0;
Time _time;
};


这样我们在定义Date类型的变量调用默认构造函数时会自动将\_year、\_month、\_day初始化为0。


==注意:这个地方我们不加初始化(因为那个地方是声明,而不是定义,所以不能称为初始化,只有在定义的时候赋值才成叫初始化),而是叫做缺省值,默认构造函数会进行使用。即等同于下面的代码:



class Date
{
public:
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year = 0;
int _month = 0;
int _day = 0;
Time _time;
};


问:C++11中的初始化和构造函数中给的缺省值哪个优先级更高呢?


答:构造函数中给的缺省值优先级更高,如果有缺省值就按照缺省值,如果没有,就按照定义时给的初始化。例如下面的代码:



class Date
{
public:
Date(int year = 1)//year既在初始化中给出了,又在构造函数参数的缺省值给出了,此时就遵循构造函数参数中的缺省值
{
_year = year;
//_month和_day都没有给缺省值,所以均按照声明时给的值,即0
}
void Print()
{
cout << _year << “-” << _month << “-” << _day << endl;
}
private:
int _year = 0;
int _month = 0;
int _day = 0;
};
int main()
{
Date d1;
d1.Print();
return 0;
}


输出结果为:


![image-20220517091604662](https://img-blog.csdnimg.cn/img_convert/dd59ae20612817d67d649cdd875decf4.png)


## 3. 析构函数


### 3.1 概念



> 
> 析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而**对象在销毁时会自动调用析构函数,完成类的一些资源清理工作**(比如释放堆区上的空间)。
> 
> 
> 
> > 
> > 问:为什么要有析构函数?为什么析构函数有时候我们还要自己进行定义?
> > 
> > 
> > 答:因为我们定义的类的变量有时候还会和一些空间产生勾连,比如我们将类变量中的某个成员变量(指针类型)指向了堆区上的一块空间,在这个类变量被销毁后,这块空间依然存在,这就造成了内存泄漏问题,且我们将再也无法找到这块空间,这块空间编译器是无法自己进行回收的,默认的析构函数也无法回收,这个只能由程序员自己来实现。其实相当于我们在面向过程中写的`Destory`函数,构造函数有点类似于`Init`函数。
> > 
> > 
> > 
> 
> 
> 


### 3.2 特性


析构函数是特殊的成员函数。 其特征如下:


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

 

class Date
{
public:
Date(int year = 0,int month = 0,int day = 0)
{
_year = year;
_month = month;
_day = day;
arr = (int*)malloc(sizeof(int) * 10);
}
void Print()
{
cout << _year << “-” << _month << “-” << _day << endl;
}
~Date()
{
cout << “~Date()函数的调用” << endl;
free(arr);//将堆区上的那块空间释放回收
arr = NULL;
}

private:
int _year;
int _month;
int _day;
int* arr;
};
int main()
{
Date d1;
return 0;
}

5. 构造与析构的顺序:**符合栈的顺序,后定义的先析构,先定义的后析构**(析构顺序和构造顺序是相反的)。  
 例如:

 

int main()
{
Date d1;
Date d2;
return 0;
}

 像上面这样进行定义,d2先析构,d1后析构。

 嵌套的类的析构顺序也是如此,我们来看下面这个代码:

 

class Time
{
public:
~Time()
{
cout << “~Time()函数的调用” << _hour << endl;
}
int _hour;
};
class Date
{
public:
~Date()
{
cout << “~Date()函数的调用” << endl;
}
Time _time1;
Time _time2;
};
int main()
{
Date d1;
d1._time1._hour = 1;
d1._time2._hour = 2;
return 0;
}

 运行截图:

 ![image-20220516163641595](https://img-blog.csdnimg.cn/img_convert/96f7076fcca86599ae6e570aecdae0bb.png)

 接下来看下一段代码:

 

class Time
{
public:
~Time()
{
cout << “~Time()函数的调用” << _hour << endl;
}
int _hour;
};
class Date
{
public:
~Date()
{
cout << “~Date()函数的调用” << endl;
}
Time _time2;
Time _time1;
};
int main()
{
Date d1;
d1._time1._hour = 1;
d1._time2._hour = 2;
return 0;
}

 运行截图:

 ![image-20220516163753272](https://img-blog.csdnimg.cn/img_convert/5518b392781e97697177a3bde7f40f0f.png)
6. **编译器生成的默认析构函数,自定义类型将不做处理,但是对会自定类型成员调用它的析构函数。**

 

class Time
{
public:
Time()
{
_hour = 0;
_minute = 0;
_second = 0;
}
~Time()
{
cout << “~Time()函数的调用” << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
arr = (int*)malloc(sizeof(int) * 10);
}
void Print()
{
cout << _year << “-” << _month << “-” << _day << endl;
}
~Date()
{
cout << “~Date()函数的调用” << endl;
free(arr);
arr = NULL;
}

private:
int _year;
int _month;
int _day;

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

	arr = (int\*)malloc(sizeof(int) \* 10);
}
void Print()
{
	cout << _year << "-" << _month << "-" << _day << endl;
}
~Date()
{
	cout << "~Date()函数的调用" << endl;
	free(arr);
	arr = NULL;
}

private:
int _year;
int _month;
int _day;

[外链图片转存中…(img-L8gdYZA7-1715673180911)]
[外链图片转存中…(img-NatkyYjJ-1715673180911)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值