目录
2、访问限定符(class默认访问权限为私有,struct默认访问权限为公有)
一、class和struct
1、在其中可以同时定义成员变量和成员函数
struct person
{
public:
void Init()
{
cout<<"zdl"<<endl;
}
void fun(int _age, char _name)
{
age = _age;
name = _name;
cout<<this->age<<endl;
}
private:
int age;
char name;
};
class person
{
public:
void Init()
{
cout<<"zdl"<<endl;
}
private:
int age;
char name;
};
小函数:想成为inline,直接在类里定义即可。
大函数:应该声明和定义分离。定义时要加作用域,表明属于哪个类域。
2、访问限定符(class默认访问权限为私有,struct默认访问权限为公有)
(1)public(公有):类内类外都可以访问。
(2)protected(保护):类内可以访问,类外不能访问。
(3)private(私有):类内可以访问,类外不能访问。
3、类的实例化
类是对对象进行描述的,是一个模型一样的东西。用类类型创建对象的过程,称为类的实例化。person类是没有空间的,只有实例化出的对象才有具体的空间。比如:一个房子的设计图,就像是一个类。你在上海根据设计图建了房子,在北京根据设计图建了同样的房子,这就叫类的实例化。
person p1;//类的实例化
实例化的每个对象的成员变量存储在类中,且都有自己独立的空间,是不同的变量。但是每个对象,调用类的成员函数都是同一个,编译链接时根据函数名去公共代码区找到函数的地址,call函数地址。且类不计函数大小。
4、this指针(是一个形参)
1、this指针在栈区,成员函数有一个隐式形参 类名*const this 只能在成员函数内部使用。
5、构造函数(一般写成全缺省的)
class Date
{
public:
Date(int year=1, int month=1, int day=1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(2023, 6, 5);
Date d3(2023, 3);
return 0;
}
注:默认生成构造函数:对内置类型成员不做初始化(因此我们要自己写构造函数)。自定义类型成员(class和struct)可以自动调用默认构造函数(它自己的构造函数)。
无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。
6、析构函数
与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
* 内置类型动态开辟的空间(malloc,new等)——显示写析构函数。
* 内置类型非动态开辟的空间——不需要显示写析构函数。编译器的默认析构函数就可以解决。
* 自定义类型成员会去调用它自己的析构函数,所有不需要显示写。如用两个栈实现队列的题中。队列类中不需要写析构函数,Stack成员会去调用它自己的析构函数。
* 先定义的后析构,后定义的先析构。
class A
{
//...
};
A aa3(3)
void f()
{
Static A aa0(0);
A aa1(1);
A aa2(2);
static A aa4(4);
}
int main()
{
f();
f();
}
构造顺序:3、0、1、2、4、1、2。静态变量在程序结束后才会销毁,所以第二次调用了f()后,静态变量就不用再次调用构造函数了。
析构顺序:~2、~1、~2、~1、~4、~0、~3。
7、拷贝构造函数
class Data
{
public:
Data(const Data &d)
{
_year = d.year;
_month = d.month;
_day = d.day;
}
private:
_year;
_month;
_day;
}
Data d2(d1);
8、深拷贝和浅拷贝
浅拷贝:默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
但是,如果在被拷贝的对象中有动态开辟的成员变量,如动态开辟的数组。在浅拷贝后,新的对象的该成员变量会指向和被拷贝对象相同的空间。这样在调用析构函数时,就会被析构两次。
所以我们就需要自己实现深拷贝来解决这个问题。(后面专门讲解)
9、运算符重载(operator)
内置类型可以直接使用运算符运算,编译器知道要如何运算。
自定义类型不可以直接使用运算符运算,编译器不知道要如何运算。
//判断两个日期是否相等
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
bool operator==(const Date& d)
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
private:
int _year;
int _month;
int _day;
};
如:用栈实现队列的题中,MyQueue中的成员变量的类型是自定义类型Stack,默认生成的赋值重载函数就可以用。前提是Stack中有正确的赋值重载函数。这样就可以自动调用。
任何一个类,只需要写一个 > == 或者 < ==重载,剩下的比较运算符重载复用即可。
Data& operator++() //前置,返回++后的
{
*this += 1;
return *this;
}
Data operator++(int) //后置,返回++前的
{
Data tmp(*this);
*this += 1;
return tmp;
}