C++入门学习3
10.类和对象
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
C++中定义类(结构体)的语法:
class className
{// 类体:由成员函数和成员变量组成}; // 一定要注意后面的分号
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。 类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。(也可以使用struct定义类,但是有一点区别,下面会说到)
类的两种定义方式:
1.声明和定义全部放在类体中,需注意:成员函数如果在类中定义,编译器可能会将其当成内联函数处理。
2. 类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
为方便书写这里使用第一种,工程中使用第二种较好。
类的访问限定符及封装
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选 择性的将其接口提供给外部的用户使用。
说明:
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果后面没有访问限定符,作用域就到 } 即类结束。
5. class的默认访问权限为private,struct为public(因为struct要兼容C)如下:struct的函数都是公有的,class从{开始到public之前的函数都是私有的。
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别(主波没理解,后续理解了再更新)
如何计算类对象的大小
如下面这个日期类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算 一个类的大小?
只保存成员变量,成员函数存放在公共的代码段
从下图可以得知,只计算成员变量的内存(按照内存对齐的方式计算:写文章-CSDN创作中心),注意空类,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。
11.this指针
先观察下面这个代码,d1和d2是如何调用Init和print函数完成初始化和打印的呢?
其实是存在一个隐藏的this指针,如下代码的注释:
class Date
{
public:void print(); // ==>void Date::print(Date *this)void Init(int year,int month,int day);// ==>void Date::Init(Date *this,int year=0, int month=1, int day=1)private:int _year;int _month;int _day;
};void Date::print() // void Date::print()==void Date::print(Date *this)
{// ==cout <<this->_year << '-' <<this->_month << '-' << this->_day << endl;cout << _year << '-' << _month << '-' << _day << endl;
}void Date::Init(int year=0, int month=1, int day=1) // ==>void Date::Init(Date *this,int year=0, int month=1, int day=1)
{_year = year;_month = month;_day = day;//this->_year = year;//this->_month = month;//this->_day = day;
}int main()
{Date d1, d2;d1.Init(2025, 8, 9); // ==> d1.Init(&d1,2025, 8, 9);d2.Init(2025, 8, 10);// ==> d2.Init(&d2,2025, 8, 10);d1.print(); // ==> d1.print(&d1);d2.print(); // ==> d2.print(&d2);return 0;
}
12.构造函数
构造函数是一个特殊的成员函数(作用是初始化对象的成员变量),名字与类名相同,创建对象时由编译器自动调用,以保证每个成员变量都有一个合适的初始值,并且在对象整个生命周期内只调用一次。前面写的日期类是使用了 Init 函数进行初始化,但是每创建一个对象就得调用这个函数才能初始化,现在使用构造函数就不需要自己手动调用函数初始化创建的对象。
特性:
1. 函数名与类名相同。
2. 无返回值。
3. 创建对象时编译器自动调用对应的构造函数。
4. 构造函数可以重载。
如下日期类作为例子:
class Date
{
public:// 1.无参构造函数Date(){_year = 0;_month = 1;_day = 1;}// 2.带参构造函数Date(int year, int month, int day) {_year = year;_month = month;_day = day;}// 参数可以全缺省//Date(int year=0, int month=1, int day=1) //{// _year = year;// _month = month;// _day = day;//}void print() // void Date::print()==void Date::print(Date *this){cout << _year << '-' << _month << '-' << _day << endl;}private:int _year;int _month;int _day;
};void TestDate()
{Date d1; // 调用无参构造函数// Date d1();不能加括号,否则就成了函数声明d1.print();Date d2(2015, 1, 1); // 调用带参的构造函数d2.print();
}
int main()
{TestDate();return 0;
}
13.析构函数
与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。比如:在栈中我们在申请了堆上面申请了空间存储栈的元素,此时调析构函数就是清理这部分空间,不在像栈那样需要写一个销毁函数destro()。
特性:
1. 析构函数名是在类名前加上字符 ~。
2. 无参数无返回值类型。
3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。(后创建的对象,在程序结束时会先调用它的析构函数)下图A类先创建两个对象接着B类创建两个对象,main函数结束时,先调用B类对象的析构函数在调用A类对象的析构函数。
如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 A,B类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。(注意下图打印的指针的地址,证明了特性的第四点)