文章目录
- 一.面向对象和面向过程
- 二.类的引入——结构体
- 三.类的定义
- 1.类定义的两种方式
- 在类里面定义函数
- 在类外定义函数——类域
- 2.访问限定符
- 3.封装——面向对象的三大特性
- 4.类的实例化
- 四.类对象模型
- 求一个类的大小
- 五.this指针
- 基本认识
- 代码解读
一.面向对象和面向过程
- 面向对象:是软件开发方法中的一种。是一种对现实世界理解和抽象的方法;是思考问题相对高级的方法。在面向对象时,我们会建立对象,主要在意对象的行为。
- 面向过程:分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。是一种思考问题的基础方法。
- 我们以做一道红烧肉的实现来体现过程和对象的区别
- 面向过程: 1.去买肉,切肉 2.起锅,烧油,调糖色,3.放入锅中,加入调料调味,4.顿好出锅
- 面向对象:1.买好红烧肉 2.把红烧肉放在锅中,3.将红烧肉盛出
- 因此:面向对象开发更加方便高效,面向过程较为繁琐。
面向对象的过程更加注重对象从而更加高效,但功能的实现有时候还是需要我们自己实现。
二.类的引入——结构体
- 类定义:类 +类名+{ };
- 在C++中,结构体被升级成了类,但C语言的用法还是可以使用的.
- 证明:
struct student
{
char _name[6];
int _age;
int _number;
student* _p;
};
- 这里的结构体被升级成了类,在C++里类名是可以被用作定义变量的,但C语言中不行,当然如果你想在这里用C语言的语法也是可以的。
- 说明:变量的定义为了与函数调用变量进行区分,我们一般都在变量的前面加上 _
- 类里面是可以定义函数的
struct student
{
void Print()
{
cout << "Print" << endl;
}
char name[6];
int age;
int number;
student* p;
};
- 类被当做一个域叫做——类域
- 目前我们学过的C++的域——全局域,局部域,命名空间域,类域。
三.类的定义
- 与结构体相似:class +类名 +{} ;
- 注意:;——分号不可省
1.类定义的两种方式
在类里面定义函数
class student
{
void Print()
{
cout << "Print" << endl;
}
char _name[6];
int _age;
int _number;
student* _p;
};
在类外定义函数——类域
void student::Print()
{
cout << "Print" << endl;
}
class student
{
void Print();
char _name[6];
int _age;
int _number;
student* _p;
};
- 注意:在类外定义函数,需要在类里面声明一下这个函数,并且要在定义的时候函数名前加上类名+作用域限定符,表明这是该类的函数。
2.访问限定符
- public :公有——类外可以直接访问
- private:私有——类外不可以直接访问
- protecter:与private类似——类外不可以直接被访问(在继承中会用到)
- 注意
- 1.C++为了兼容C语法,默认结构体类的成员为公有。但在class中,默认成员为私有,不能从类外直接进行访问。
- 2.其作用范围从其出现开始到遇到下一个访问限定符/类作用域结束为止。
- 3 访问限定符只在编译期间有用,是对语法的检查,因此只在编译期间报错,在数据已经被加载到内存中时,这时看访问限定符其实是一样的,访问限定符更像是一个门槛,一个前提。
3.封装——面向对象的三大特性
- 封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
- 说明:隐藏对象的属性和实现细节——是通过private进行实现的。外部接口——实现的函数。
- 封装是为了更好的管理和保护数据,避免对数据进行破坏。因为会调用接口进行管理数据,这里的接口就是我们设置的规则。并且接口一般都是公开的。
4.类的实例化
- 定义一个类就如同定义了一个结构体,跟结构体的自定义类型相似,我们定义类时只是说明了类里面有什么内容,并不能直接使用类的内容,那与定义一个结构体变量就如同类的实例化。
- 这也间接反映出了域里面并不一定都是变量的定义,也有可能都是变量的声明。
class student
{
public:
void Print();
private:
char name[6];
int age;
int number;
student* p;
};
int main()
{
student A;//这样我们就完成了类的实例化。
return 0;
}
四.类对象模型
求一个类的大小
- 与求结构体的大小相似:
1.第一个成员在与结构体变量偏移量为0的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
说明: 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
- 需要我们注意的是在C语言中结构体中至少定义一个变量,而在C++中结构体被升级为类,可以为空,甚至可以在里面定义函数。
- 那函数占不占内存呢?
实验便知:
class student1
{
public:
void Print()
{
cout << "Print" << endl;
}
private:
int age;
};
class student2
{
public:
void Print()
{
cout << "Print" << endl;
}
};
class student3
{
};
int main()
{
cout << sizeof(student1) << endl;
cout << sizeof(student2) << endl;
cout << sizeof(student3) << endl;
return 0;
}
- 通过student2与student3进行对比可说明——函数是不占用类里的空间。
- 函数占用的空间在哪呢?——公共代码段(当执行时,调用此函数的代码执行即可)
- 说明:不同的对象调用的函数是一样的。
说明:这样设计是为了空间的节省,不同对象调用的函数是一样的,在调用时执行函数的代码即可,这样一码多用节省了空间。
- 通过student1与studen2进行对比可简单的说明——对齐方式与结构体的对齐方式一致。
- student3的大小为1——说明类的大小最小为1,至于为什么不能为0,假设如果为0,则开辟空间为0,通过以前的学习可知,一个变量的开辟是必须有地址的,0空间无地址,互相矛盾,因此不能为0,这里的1充当的是占位符的作用。
五.this指针
C++中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参
数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该
指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
基本认识
class Date
{
public:
void Print()//括号里面相当于(this),这里的this也是不能写出来的,也是隐式转换
{
cout << _year <<"-"<< _month<<"-" <<_year<< endl;
//在函数里面可以使用this指针,上面的代码写完整就是
cout <<this-> _year <<"-"<<this-> _month<<"-" <<this->_year<< endl;
//this=NULL 这也是不行的,因为this指针是一个const修饰的类型,不能进行修改。
//说明定义一个变量int * const this,const具有就近原则
//放在this前this本身不能修改
//放在*前说明*this不能被修改
}
void Init(int year = 1 ,int month =1,int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _day;
int _month;
int _year;
};
int main()
{
Date A;
A.Init();
//A.Init(this);这样写是不对的,因为this是隐式转换的,这里的this转换为A的地址;
//A.Init(&A);其实就转换为了指定对象的地址,怎么进行转换呢?A.这里通过A就能找到A的地址。
A.Print();
return 0;
}
- 我们看这里的函数调用,为什么不用通过传类变量的地址,直接就能使用类里面的变量?
- 说明:这里隐含了一个this指针,那this指针是什么呢?
- 其实这里的this指针就是类实例化的地址。
- 总结:this指针不能显示传参, 函数定义的参数处也不能显示出来,函数内部可以使用访问对象的变量,但对this指针不能进行修改。
代码解读
struct A
{
public:
void PrintA()
{
cout << _a << endl;
//这里_a相当于this->_a发生了对空指针解引用的情况
}
int _a;
};
int main()
{
A* p = NULL;
p->PrintA();
//说明这里p->PrintA()等于(*p).PrintA()
//但这里并没有语法错误:因为这里是调用里面函数,函数并不占用类的空间,因此可以调用类的函数
//但是这里将p传进去了,p是一个空指针,在里面访问变量时,会产生对空指针解引用的情况,因此
//具体的报错位置在函数里面的变量使用
}
- 代码是否报错?具体报错在哪?
- 报错,报错位置在函数里面。
struct A
{
public:
void Show()
{
cout << "Show()" << endl;
}
int _a;
};
int main()
{
A* p = NULL;
p->Show();
}
-
代码是否报错?具体报错在哪?
-
不会报错,因为函数里面没有对变量的访问。
-
总结:this指针可以为空,但必须通过对象进行调用函数。