学习“类”不“类”,有“对象”了吗?
目录
面向过程和面向对象
类的引入
类的定义
访问限定符
封装
类的作用域
类的实例化
类对象的存储方式
计算类对象的大小
this指针
this指针的特性
this指针两问
面向过程和面向对象
以考驾照为例:面向过程在意的是你在驾校练车的每一次练车,考试时的每一个操作步骤,踩离合、松手刹、踩油门.......
整个考试过程是这三个对象交互完成的,驾驶员在车上“眼花缭乱”的操作,教练员怎样保护我的安全,车子做出怎样的反映,这些都不需要关心!
类的引入
如果把学生定义成一个类,你们学校的每个同学都是一个对象。每个学生的姓名,学号,家庭住址都是成员变量。学习、运动、谈恋爱就是成员方法(函数)。
你不屑的瞟了一眼,“就这”!我用C语言结构体就能搞定呀!C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
#include "add.h"
using namespace std;
struct Student
{
//学习
void Study()
{
//...
}
//运动
void Exercise()
{
//...
}
//谈恋爱
void Love()
{
//...
}
char* name;
char* add;
int number;
};
int main()
{
Student LiSi;
LiSi.Study();
LiSi.Exercise();
LiSi.Love();
return 0;
}
在C++中更喜欢用class关键字来代替上面结构体的定义!
类的定义
class Student
{
// 类体:由成员函数和成员变量组成
};
class Student
{
public:
void Init(int year,int month,int day)
{
//...
}
private:
int _year;
int _month;
int _day;
};
●类声明在.h文件中,定义在.cpp文件中
Student.h
class Student
{
public:
void Init(int year,int month,int day);
private:
int _year;
int _month;
int _day;
};
Student.cpp
void Student::Init(int year,int month,int day)
{
//...
}
既然struct可以定义类,c++为什么要引入class关键字呢?
C++ 需要兼容 C 语言,所以 C++ 中 struct 可以当成结构体使用,也 可以用来定义类,和class 定义类是一样的。区别是 struct 定义的类默认访问权限是 public , class 定义的类默认访问权限是private。 ( public和private是两个访问限定符)class Data { void Init(int year,int month,int day) { _year = year; _month = month; _day = day; } void print() { cout << _year << "年" << _month <<"月" << _day <<"日" << endl; } int _year; int _month; int _day; }; int main() { Data d1; d1.Init(2001,10,3); d1.print(); d1._year = 2022; d1._month = 12; d1._day = 20; d1.print(); return 0; }
class定义的类默认访问权限是private!
struct 定义的类默认访问权限是public!
struct Data { void Init(int year,int month,int day) { _year = year; _month = month; _day = day; } void print() { cout << _year << "年" << _month <<"月" << _day <<"日" << endl; } int _year; int _month; int _day; }; int main() { Data d1; d1.Init(2001,10,3); d1.print(); d1._year = 2022; d1._month = 12; d1._day = 20; d1.print(); return 0; }
访问限定符
class Data
{
public:
void Init(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "年" << _month <<"月" << _day <<"日" << endl;
}
int _year;
int _month;
int _day;
};
int main()
{
Data d1;
d1.Init(2001,10,3);
d1.print();
d1._year = 2022;
d1._month = 12;
d1._day = 20;
d1.print();
return 0;
}
class Data
{
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;
};
封装
定义:封装本质上是一种管理,让用户更方便使用类 。将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
在 C++ 语言中实现封装,可以 通过类将数据以及操作数据的方法进行有机结合,通过访问权限来 隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用 。
类的作用域
class Data
{
public:
void Init(int year, int month, int day);
private:
int _year;
int _month;
int _day;
};
//Init是属于Data这个类域
void Data::Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
类的实例化
简单来说,就是用类类型创建对象(变量)的过程,称为类的实例化。
●类是对对象进行描述的,是像模型一样的东西,就比方是工匠造家具的图纸,这个图纸只是设计好需要那些材料,建造方法。但是并没有实际建造出来。所以并没有给类分配实际的内存空间!
下面的写法就是错误的,Student就好比是“图纸”。
struct Student
{
//学习
void Study()
{
//...
}
char* name;
char* add;
int number;
};
int main()
{
Student.numner = 20060240;
return 0;
}
class Chair
{
public:
void Init(char color,char type,int price)
{
_color = color;
_type = type;
_price = price;
}
void Printf()
{
cout << " 椅子颜色:" << _color <<
" 椅子型号:" << _type <<
" 椅子价格:" << _price << endl;
}
private:
char _color;
char _type;
int _price;
};
int main()
{
Chair c1;
c1.Init('R', 'X', 66);
c1.Printf();
Chair c2;
c2.Init('B','Y',99);
c2.Printf();
return 0;
}
类对象的存储方式
只保存成员变量,成员函数存放在公共的代码段中!
还是以学生类为例,每个学生都是一个对象,他们的性格,特征确实各不相同。但是,他们都需要吃饭,这个时候如果给每个学生都建造一个食堂,就是一种资源浪费!只需要在一块土地上建一个食堂,用餐的时候,所有同学都过来就好了。
计算类对象的大小
●一个类的大小,实际就是该类中” 成员变量”之和,当然要注意内存对齐 。内存对齐在之前的博客提到过:自定义类型:结构体、枚举、联合体_Bug程序员小张的博客-CSDN博客结构体https://blog.csdn.net/weixin_59351791/article/details/126901411?spm=1001.2014.3001.5502 ●计算类对象的大小情况1:类中既有成员变量,又有成员方法。class A { public: void Init() { //... } private: char a1; char a2; int age; }; int main() { A a; cout <<"类对象的大小为:" << sizeof(a) << endl; return 0; }
情况2:类中只有成员方法。class A { public: void Init() { //... } void Print() { //... } }; int main() { A a; cout << "类对象的大小为:" << sizeof(a) << endl; return 0; }
情况3:空类。空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象!class A { }; int main() { A a; cout << "类对象的大小为:" << sizeof(a) << endl; return 0; }
this指针
在回过头看刚刚的一份代码,Date类中有 Init 与 Print 两个成员函数,函数体中没有关于不同对象的区分,那当d1调用 Init 函数时,该函数是如何知道应该设置d1对象,而不是设置d2对象呢?
class Data
{
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()
{
Data d1;
d1.Init(2001, 10, 3);
d1.print();
Data d2;
d2.Init(2020, 11, 4);
d2.print();
return 0;
}
C++中引入了 this指针,编译器给每个“ 非静态的成员函数“增加了一个 隐藏的指针参数,让 该指针指向当前对象(函数运行时调用该函数的对象), 在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,用户不需要来传递,编译器自动完成。
this指针的特性
●this指针的类型:类类型* const,在成员函数中不能给this指针赋值。
const只修饰其后的变量,至于const放在类型前还是类型后并没有区别。
●只能在成员函数的内部使用。
●this指针本质上是成员函数的形参,当对象调用成员函数时,将对象的地址作为实参传递给形参。对象中不存储this指针。
●this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传
递,不需要用户传递。
this指针两问
●this指针存放在哪里?
答:this指针本质上是成员函数的形参,一般是存在栈帧中(vs下进行了优化,使用ecx寄存器传递)。
●this指针可以为空吗?
答案:可以
例题1.下面程序编译运行结果是?
class A { public: void Print() { cout << "Print()" << endl; } private: int _a; }; int main() { A* a1 = nullptr; a1->Print(); return 0; }
A、编译报错 B、运行崩溃 C、正常运行答:正常运行分析:在上面的代码中,虽然通过空指针调用了成员函数,但是成员函数的地址不在对象中。而是在公共代码区域!例题2. 下面程序编译运行结果是?class A { public: void PrintA() { cout<<_a<<endl; } private: int _a; }; int main() { A* p = nullptr; p->PrintA(); return 0; }
A、编译报错 B、运行崩溃 C、正常运行答:运行崩溃分析:看起来和第一题很像,但是在成员函数中访问了p对象的成员变量,空指针解引用,程序会崩溃。