类和对象(上)——概念和定义

一、初识类和对象

  C++是基于面向对象的语言,但并不是纯面向对象的语言,其中还包含了C的部分,而C是面向过程的语言。

  1. 类:类是现实世界在计算机中的反映,它将数据和对这些数据的操作封装在一起(并没有开空间)
    在这里插入图片描述
  2. 对象:类的实例(占有实际的空间)
    在这里插入图片描述
    在C语言中不能再结构体中包含函数,但是在C++中却可以。
struct Student  //定义一个类
{
   public:
   	student()//成员函数
   	{}
   private:
   	int _age;//年龄
   	int _sto;//学号
   	char* _name;//姓名
};
int main()
{
   student s1;
   s1._age=19;
   s1._sto=1705990449;
   s1._name="张三“;
}

我们可以看到,在这个类中,我们定义了一个函数student,我们习惯称它为“方法”,但是在C++中,更喜欢用“class”来替换“struct”。

二、类的定义

class className
{
 // 类体:由成员函数和成员变量组成
 
}; // 一定要注意后面的分号

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号。
类中的元素称为类的成员:类中的数据称为类的属性或者成员变量; 类中的函数称为类的方法或者成员函数
类的两种定义方式:

  1. 声明和定义全部放在类体中,需要注意:成员函数如果在类中定义,编译器可能会将其当成内联函数(详情请看C++入门)处理。
//类的声明和定义全部放在类中
class Student  //定义一个类
{
  public:
   void showstudent()//成员函数
   {
   	cout<<_age<<_sto<<_name;
   }
       
  private:
   int _age;//年龄
   int _sto;//学号
   char* _name;//姓名
};
int main()
{
  student s1;
  s1._age=19;
  s1._sto=1705990449;
  s1._name="张三“;
  s1.showstudent();
}
  1. 声明放在.h文件中,类的定义放在.cpp文件中
//类的声明放在.h文件中
class Student  //定义一个类
{
  public:
   void showstudent();//成员函数
  
  private:
   int _age;//年龄
   int _sto;//学号
   char* _name;//姓名
};
//定义放在类的实现文件中”student.cpp“
#include"student.h"
void showstudent()
{
  cout<<_age<<_sto<<_name;
}

一般来说,更希望使用第二种方法定义类!

三、类的访问限定符和封装

C++的三大特性:封装,继承,多态。在此,我们先介绍封装的概念,其余两个我们后面在做讲解。
封装概念:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。我相信大家也是和我第一次看一样,一脸懵逼…,这里我给大家举一个例子:博物馆是如何管理的呢?如果不采取措施,文物将被损坏。所以我们就建立了一个博物馆,把文物都封装起来,不让人们看。但是我们开放了购票机制,可以买票突破封装进去参观。这就和类一样,我们使用类数据和方法都封装到一下。不想给别人看到的,我们使用protected/private把成员封装起来。开放一些共有的成员函数对成员合理的访问。所以封装本质是一种管理。

类的三个访问限定符:
在这里插入图片描述

【访问限定符说明】

  1. public修饰的成员在类外可以直接被访问
  2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
  3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
  4. class的默认访问权限为private,struct为public(因为struct要兼容C),这也是struct和class的区别
    注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别

四、类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员,需要使用 :: 作用域解析符指明成员属于哪个类域。

// 这里需要指定showstudent是属于Student这个类域
void Student::showstudent()
{
 cout<<_age<<" "_sto<<" "<<_age<<endl;
}

类的访问限定符体现了类的封装性
在这里插入图片描述
因为_name是私有的,所以在类外不能使用,而_age,Eat()是公有的,所以在类外可以直接使用。

五、类的实例化

用类类型创建对象的过程,称为类的实例化

  1. 类只是一个模型一样的东西,限定了类有哪些成员,定义出一个类并没有分配实际的内存空间来存储它
  2. 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
  3. 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间。在这里插入图片描述

六、类的大小

一个类中既包含了成员函数,又包含了成员变量,那么一个类的实例化对象中包含了什么?怎么计算一个类的大小?

只保存成员变量,成员函数存放在公共的代码段,通过指针的方式来找到成员函数。当然这里也遵循内存对齐原则。

在这里插入图片描述

class A1 {
public:
   void f1() {}
private:
   int _a;
};

// 类中仅有成员函数
class A2 {
public:
 void f2() {}
};

class A3
{};

在这里插入图片描述
空类的大小:空类占1个字节,占位用,告诉系统我这里定义了一个类,虽然它是空的。

结论:一个类的大小,实际就是该类中”成员变量”之和,当然也要进行内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类。

结构体内存对齐规则

结构体内存对齐和大小端的问题

七、this指针


class Date
{ 
public :
 void Display ()
 {
 	r<< "-" <<_month << "-"<< _day <<endl;
 }
 
 void SetDate(int year , int month , int day)
 {
	 _year = year;
	 _month = month;
	 _day = day;
	 }
private :
	 int _year ; // 年
	 int _month ; // 月
	 int _day ; // 日
};
int main()
{
	 Date d1, d2;
	 d1.SetDate(2018,5,1);
	 d2.SetDate(2018,7,1);
	 d1.Display();
	 d2.Display();
return 0; 
}

Date类中有SetDateDisplay两个成员函数,函数体中没有关于不同对象的区分,那当s1调用SetDate函数时,该函数是如何知道应该设置s1对象,而不是设置s2对象呢?

C++中通过引入this指针解决该问题:
  C++编译器给每个“成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象,在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。

this指针的特性

  1. this指针的类型:类类型* const
  2. 只能在“成员函数”的内部使用
  3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针
  4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
void Display()
{
	cout<<_year;
}
void Display()
{
	cout<<this->_year;//这就是编译器隐藏的this指针,不过不用写也没事,编译器会自动添加
}

this指针存在哪里?
其实编译器在生成程序时加入了获取对象首地址的相关代码。并把获取的首地址存放在了寄存器ECX中(VC++编译器是放在ECX中,其它编译器有可能不同)。也就是成员函数的其它参数正常都是存放在栈中。而this指针参数则是存放在寄存器中。 类的静态成员函数因为没有this指针这个参数,所以类的静态成员函数也就无法调用类的非静态成员变量。
this指针可以为空吗?

void test(person *const this)
{}
void change(person *const this)
{
 cout<< name<< endl;
}

根据_thiscall调用约定可知:
(1)_thiscall只能用在类的成员函数上。
(2)参数从左向右压栈。
(3)如果参数个数确定,this指针通过ecx传递给被调函数,如果不参数确定,this指针所在的参数被压栈后压入堆栈。
(4)对于参数不定的,调用者清理堆栈,否则函数自己清理堆栈。
可以看出每次成员函数调用时,this都会被压入。
所以:

当把空的this传入 test(NULL)时:

然后 test没有调用任何函数,所以执行函数。

当把空的this传入 change(NULL)时:

然后 change通过this指针调用this->name,而this是空的,所以出错了

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值