众所周知C语言是面向过程的编程语言,关注的是过程;解决问题前,需要分析求解的步骤,然后编辑函数逐步解决问题。C++是基于面向对象的,关注的是对象,将一件事拆分成不同的对象,不同对象间交互解决问题。
一、类的引出
C语言结构体只能定义变量;而在C++中,结构体不仅可以定义变量,也可以定义函数。在前期的博文《C语言实现栈》中,使用C语言实现栈,此时结构体内只能定义变量来构造结构体的成员;而C++中,在struct定义的结构体内可以构造成员函数。具体如下所示:
struct stack
{
public:
stack()
:_capacity(_capacity==0?4:2*_capacity)
,_a(new int[_capacity])
,_top(0)
{}
void push(const stack& s);
void pop();
bool empty();
int top();
int size();
~stack()
{
delete[] _a;
_a = nullptr;
_top = _capacity = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
其中,在public中 stack( )、 ~stack( ) 为成员构造函数、成员析构函数,push( )等为成员函数;而在private中的都为成员变量。
二、类的定义
2.1 什么是类
类是C++的核心特性,通常被称为用户定义的类型。类用于指定对象的形式,它包含了数据表示方法和处理数据的方法。
2.2 类的语法
class className
{
//类体由成员函数和成员变量组成
};//分号不可遗漏
class为定义类的关键字,classname为类的名字,{ }中为类的主体;此外,类的{ }结束后的分号不可省略。
类主体内容称为类的成员:类中变量称为类的属性或成员变量;类中函数称为类的方法或成员函数。
类的两种定义方式:
- 声明和定义全部放在类体中;
- 声明与定义分离;类声明放在.h文件中,成员函数放在.cpp文件中;此外,在.cpp定义文件中,定义成员函数时,需要在函数名前、返回值类型后添加类名::
//声明与定义全在类体中
struct Date
{
public:
Date(int year = 2000,int month = 10,int day = 1)
:_year(year)
,_month(month)
,_day(day)
{}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print()
{
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
~Date()
{
_year = 0;
_month = 0;
_day = 0;
}
private:
int _year;
int _month;
int _day;
};
第二种声明与定义分离方式为:
//stack.h文件
struct Date
{
public:
Date(int year = 2000, int month = 10, int day = 1);
Date(const Date& d);
void Print();
~Date();
private:
int _year;
int _month;
int _day;
};
//stack.cpp文件
Date::Date(int year , int month , int day )
:_year(year)
, _month(month)
, _day(day)
{}
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Date::Print()
{
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
Date::~Date()
{
_year = 0;
_month = 0;
_day = 0;
}
三、类的访问限定符号及封装
3.1 访问限定符
C++实现封装的方式:用类将对象的属性与方法结合在一起,让对象更加完善,通过访问权限选择性的将其接口提供给外部用户使用。
访问限定符说明:
-
public
修饰的成员在类外可以直接被访问; -
protected
和private
修饰的成员在类外不能直接被访问; -
访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止;
-
如果后面没有访问限定符,作用域就到
}
即类结束; -
class的默认访问权限为
private
,struct为public
(因为struct要兼容C)。
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别。
3.2 封装
面向对象的三大特性:封装、继承、多态。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
封装本质上是一种管理,让用户更方便使用类。在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。
四、类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用::
作用域操作符指明成员属于哪个类域。
在下列个人信息类中,add( )属于person这个类域;个人的成员变量也属于这个类域。
class person
{
public:
void add();
private:
int _year;
int _sex;
int _height;
int _weight;
int _tel;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
五、类的实例化
用类类型创建对象的过程,称为类的实例化。
-
类是对对象进行描述的,是一个模型一样的东西,限定了有哪些成员,定义出一个类并没有分配实际的内存空间来存储它;
-
一个类可以实例化多个对象,实例化 出的对象,占用实际的物理空间,存储类成员变量;
class person
{
public:
void PrintPersonInfo();
private:
char _name[20];
char _gender[3];
int _age;
};
void Person::PrintPersonInfo()
{
cout<<_name<<" "<<_gender<<" "<<_age<<endl;
}
注意:person类是没有空间的,只有person类实例化出来的对象才占用空间。
六、类对象模型
在Date类中,既有成员函数又有成员变量;那么Date类占用多少存储空间?
int main()
{
Date d1;
cout << "d1所占空间:" << sizeof(d1) << endl;
return 0;
}
Date类对象d1初始化后,占用了12字节;若Date类没有初始化对象,不会占用存储空间。
6.1 类对象的存储
- 对象中包含类的各个成员
-
代码只保存一份,在对象中保存存放代码的地址
-
只保存成员变量,成员函数存放在公共的代码段
6.2 结构体内存对齐规则
-
第一个成员与结构体偏移量为0的地址处;
-
其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处;
-
结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍;
-
如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
注意:对齐数=编译器默认的一个对齐数与该成员大小的较小值;VS中默认的对齐数为8。
七、this指针
7.1 类中this指针的使用
在下列Date类中,有Init和Print两个成员函数,函数体中没有关于不同对象的区分,若对象d1需要调用这两个函数时,C++引入了this指针。C++编译器给每个非静态的成员函数增加了一个隐藏的指针参数,让this指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。这些操作不需要用户操作,编译器自动完成。
class Date
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout <<_year<< "-" <<_month << "-"<< _day <<endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1, d2;
d1.Init(2022,1,11);
d2.Init(2022, 1, 12);
d1.Print();
d2.Print();
return 0;
}
7.2 this指针的特性
- this指针的类型:类类型 const*,即成员函数中,不能给this指针赋值;
- 只能在“成员函数”的内部使用;
- this指针本质上是“成员函数”的形参,当对象调用函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针;
- this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。