俺来写笔记了,哈哈哈,浅浅介绍类和对象的知识点!
目录
1.类的6个默认成员函数
俺们定义一个空类:
class N
{
};
似乎这个类N里面什么都没有,其实不是这样子的。这个空类有6个默认的成员函数 。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
其实不单止是空类,任何类在什么默认成员函数都不写时,编译器会自动生成以上6个默认成员函数。任何类如果没有显式实现上面的6个默认成员函数的某些个默认成员函数,编译器就会自动生成那些没有显式实现的默认成员函数。
如果懵懵的,没关系,下面鼠鼠会详细介绍!
2.构造函数
注意:构造函数是默认成员函数之一。
2.1.构造函数的概念
我们创建对象很多时候都希望对象使用前能被初始化,如果还是用之前学的Init方法初始化,如下:
class people
{
const char* _name;
int _age;
public:
void Init(const char*name,int age)
{
this->_name = name;
this->_age = age;
}
};
int main()
{
people HD;
HD.Init("HD", 20);
people LCD;
LCD.Init("LCD", 20);
return 0;
}
每次创建对象都要调用Init,太麻烦,所以C++的类有了构造函数:
构造函数简单来说就是初始化用的:构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象(类的实例化或者对象实例化)时由编译器自动调用,以保证每个数据成员都有一个合适的初始值,并且在对象整个生命周期内只调用一次。而且C++规定,对象实例化必须调用构造函数。
构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任 务并不是开空间创建对象,而是初始化对象。开空间创建对象那是类的实例化(或者说对象实例化)。
附:类的实例化具体知识请看【C++】类和对象1.0
2.2.构造函数的特性
特性如下,有7点:
- 特性1:函数名与类名相同。
- 特性2:构造函数可以重载,可以有参数也可以没有参数。
- 特性3:无返回值,这里不是void,是就不需要写返回值。
- 特性4:对象实例化(也就是类的实例化)时编译器自动调用对应的构造函数。
举个栗子来印证上面4点特性:
class people
{
const char* _name;
int _age;
public:
people()//无参的构造函数
{
this->_name = "HD";
this->_age = 20;
}
people(const char* name,int age)//带参的构造函数
{
this->_name = name;
this->_age = age;
}
};
int main()
{
people HD;//调用无参的构造函数
people LCD("LCD",20);//调用带参的构造函数
return 0;
}
我们看people类里面本鼠显示实现了2个构造函数:一个无参、另一个带参, 函数名与类名(people)相同,无返回值,这2个构造函数构成函数重载。对象实例化时自动调用相应的构造函数。
附:函数重载知识请看【C++】C++入门1.0
同志们不相信对象实例化时自动调用相应的构造函数的话,大可去调试一下。本鼠就将调试模式下的监视窗口打开给同志们看看,可以看到确实初始化了:
我们再注意主函数内的写法:
int main()
{
people HD;//调用无参的构造函数
people LCD("LCD",20);//调用带参的构造函数
return 0;
}
奇奇怪怪的吧?但是语法就是这样的。 第一条语句类实例化对象HD时会自动调用无参的构造函数,因为没有像第二条语句那样给参数。第二条语句类实例化对象LCD时会自动调用带参的构造函数,因为对象名后面跟了("LCD",20),这个东西就是传递给带参的构造函数做形参的。
注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明。
错误写法:
class people
{
const char* _name;
int _age;
public:
people()//无参的构造函数
{
this->_name = "HD";
this->_age = 20;
}
};
int main()
{
people HD();//warning C4930: “people HD(void)”: 未调用原型函数(是否是有意用变量定义的?)
return 0;
}
其实上面的2个构造函数我们可以合成1个有全缺省参数的构造函数,功能也是一模一样的,如下:
附:缺省参数知识请看【C++】C++入门1.0
class people
{
const char* _name;
int _age;
public:
people(const char* name="HD", int age=20)//全缺省参数的构造函数
{
this->_name = name;
this->_age = age;
}
};
int main()
{
people HD;
people LCD("LCD",20);
return 0;
}
需要注意的是:全缺省参数的构造函数和无参的构造函数理论上是构成函数重载,是可以同时存在的。但实际上它们2个函数却不能同时存在,因为对象实例化时可能存在歧义。比如:
class people
{
const char* _name;
int _age;
public:
people(const char* name = "HD", int age = 20)//全缺省参数的构造函数
{
this->_name = name;
this->_age = age;
}
people()
{
this->_name = "HD";
this->_age = 20;
}
};
int main()
{
people HD;//error C2668: “people::people”: 对重载函数的调用不明确
people LCD("LCD", 20);
return 0;
}
编译报错,因为实例化对象HD时,编译器不知道该调用哪一个构造函数!!
- 特性5:如果类中没有显式定义任何一个构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义了任何一个构造函数,编译器将不再生成。
class people
{
const char* _name;
int _age;
public:
people(const char* name , int age )
{
this->_name = name;
this->_age = age;
}
};
int main()
{
people HD;//error C2512: “people”: 没有合适的默认构造函数可用
return 0;
}
看到这里报错了:已经显示定义了一个构造函数,那么编译器不再生成无参的默认构造函数。导致对象HD实例化时没有合适的默认构造函数可用。
- 特性6:无参的构造函数和全缺省参数的构造函数都称为默认构造函数,所以默认构造函数有3个:无参的构造函数、全缺省参数的构造函数、我们没写编译器默认生成的构造函数(无参的,特性5介绍过)。并且默认构造函数只能有一个。
默认构造函数说白了就是不用传参就可以直接使用的构造函数,编译器知道如何使用的构造函数。
为什么默认构造函数只能有一个?
前面我们介绍了全缺省参数的构造函数和无参的构造函数不能共存,并且一旦用户显式定义了任何一个构造函数,编译器不再生成默认的构造函数。那么这3个默认构造函数两两互斥,所以只能有一个。
那么我们来探讨一下编译器生成的默认构造函数对于对象内不同类型的成员会有什么行为?
- 特性7:编译器生成的默认构造函数对于对象内内置类型成员不做处理(不排除有些编译器会去处理对象内内置类型成员,处理方式未知),对于对象内自定义类型成员会去调用它的默认构造函数(俺可没说对于对象自定义类型成员会去调用它的编译器生成的默认构造函数哈,俺说的是会去调用它的默认构造函数)。
补充知识:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类 型,如:int/char...自定义类型就是我们使用class/struct/union等自己定义的类型。
class Time
{
int _hour;
int _minute;
int _second;
public:
Time()
{
this->_hour = 12;
this->_minute = 20;
this->_second = 5;
}
};
class Date
{
//内置类型
int _year;
int _month;
int _day;
//自定义类型
Time _t;
};
int main()
{
Date d;
return 0;
}
同志们看好了。编译器生成的默认构造函数对于对象d的内置类型成员不做处理,所以可以看到_year、_month、_day被初始化成了随机值;对于自定义类型成员_t回去调用了它的默认构造函数Time(),所以可以看到_hour、_minute、_second被初始成了12、20、5。
注意:C++11 中针对对象内置类型成员不做处理的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值(缺省值)。
class Time
{
int _hour;
int _minute;
int _second;
public:
Time()
{
this->_hour = 12;
this->_minute = 20;
this->_second = 5;
}
};
class Date
{
//内置类型
int _year = 2024;
int _month = 6;
int _day = 17;
//自定义类型
Time _t;
};
int main()
{
Date d;
return 0;
}
同志们看:因为内置类型成员在类中声明时给了默认值,所以编译器生成的默认构造函数将对象内置类型成员初始化成了默认值。。。
介绍完构造函数,我们要明白需要具体分析对象的初始需求,需要我们自己显式定义构造函数我们就自己定义,不需要就让编译器自动生成。但是大多数情况下都是要我们显式定义的。
2.3.构造函数的调用顺序
对象先定义的先构造,后定义的后构造。
比如:
#include<iostream>
using namespace std;
class Date
{
int _year;
public:
Date(int n)//构造函数
{
cout << "Date:" << n << endl;
}
};
void Test()
{
static Date d9(9);
Date d10(10);
}
const Date d1(1);
static Date d2(2);
Date d3(3);
static Date d4(4);
int main()
{
Date d5(5);
const Date d6(6);
static Date d7(7);
D