面向对象:行为和属性
人的属性:性别,爱好,家庭住址等
人的行为:打羽毛球,上班,吃饭,睡觉等
行为需要用方法来实现,属性需要变量来定义
实例化:需要去创建对象。通过对象访问成员方法和成员变量。
面向对象
有对象一定有空间
有空间不一定有对象
为什么创建一个空对象 ,其所占字节数为1?
原因:C++ 编译器会给每个空对象也分配一个字节空间,是为区分空对象占内存位置。
- 类在设计的时候,可以对属性和行为放在不同的权限下,加以控制
访问权限有三种:(访问限定符)
public
公有权限 类内 可以访问 类外可以访问protected
保护权限 类外不可以 儿子可以访问父亲中的保护内容private
私有权限 除了类内 均不可访问
(三种权限体现了封装性)
如下:
class Goods
{
public:
int size;
private:
int num;
protect:
int age;
}; //分号不能少,这是一条说明语句
int main()
{
Goods good;
good.size =10; //可以访问
good.num =100; //不可以
good.age =10; //不可以
}
如下:成员属性和和成员方法被封装成为一个整体。
class Goods
{
private:
int Amout;
char Name[LEN];
float Price;
public:
//内联 inline void RegisterGoods(char[],int ,float); //输入数据
char GetName(char[]); //读取商品名
float GetPrice(void); //读取商品单价
};
inline void Goods::RegisterGoods(const char *name,int amout ,float price)
{
strcpy_(Name,LEN,name);//字符串拷贝函数,拷贝的长度
Amout = amout;
Price = price; //实现了类中的私有属性的访问。
}
char Goods::GetName(char name[]) //读取商品名
{
strcpy(Name,name);
}
- 3个数据成员被说明为私有,而3个函数成员被说明为公有;这就是说,如果从外部对3个数据成员来操作的话,只能通过3个公有函数来完成,数据受到了良好的保护,不易受到外部环境的影响。共有函数集定义了类的接口。
- 类是一种数据类型,定义时,系统不为类分配空间,所以不能对类的数据成员进行初始化,类中的任何数据成员也不能使用关键字extern,auto或register限定其存储类型。
如果要访问类中的成员,必须实例化对象。 - 成员函数的定义在类的说明之后:
返回类型 类名::函数名(参数列表)
如:(同返回类型,同参数列表 )
};
void Goods::GetName(char[]){ 函数体}
【使用对象,可以调用成员方法访问私有属性。】
类中的成员属性存在数据区,成员方法存在代码区
- 那么内存是如何给每一个对象分配空间呢?
如下图:
每一个对象共享同一个方法,每个对象调用方法的,会去给各自访问的成员属性开辟空间。
也就是:【内存为每个对象开辟一个数据区,共享一个代码区。】 - 那每个对象如何知道自己通过成员方法访问的成员属性是自己的呢?
那就要说this指针的使用:
- 第一步,识别和记录 类体中【属性名称,类型,访问限定符】,不管属性定义在类中的哪个位置,都会先识别属性。对私有化的【数据区】数据进行处理。
- 第二步,识别和记录 类体中函数原型(返回类型,函数名,形参列表), 形参的默认值,访问限定符,不识别函数体。对代码区的函数声明进行处理
只识别声明,不识别函数体,那函数定义的时候,调用函数,就不需要考虑位置。
如下:
class stu
{
private:
char Name[10];
char Sex[10];
public:
char GetName(char[]);
char GetSex(char[]);
}
char stu::GetName(char *name)
{
this->GetSex();//因为已经在第二部识别记录过类中函数的声明,所以加上this指针,即便GetSex在GetName后定义,也能调用。
}
char stu::GetSex(char *sex)
{
this->Sex = sex;
}
- 第三步,改写类中定义的函数的形参列表和函数体,改写对象调用成员函数的形式。(就是加上this指针) 其实就相当于把对象的地址传给this指针,这样就相当于每个对象去控制各自私有成员属性。 对成员函数进行改写
如下:
class Goods
{
public:
void RegisterGoods(const char *name,int amout ,float price);
void RegisterGoods(Goods *this,const char *name,int amout ,float price) ;
void CountTotal(void);
void CountTotal(Goods *this);
};
改写函数体
void Goods::RegisterGoods(const char *name,int amout ,float price)
{
strcpy_(Name,LEN,name);//字符串拷贝函数,拷贝的长度
Amout = amout;
Price = price; //实现了类中的私有属性的访问。
}
void Goods::RegisterGoods(Goods *this,const char *name,int amout ,float price)
{
strcpy_(this->Name,LEN,name);//字符串拷贝函数,拷贝的长度
this->Amout = amout;
this->Price = price; //实现了类中的私有属性的访问。
}
int main()
{
Goods book,tea; //对象没有this指针,是在调用的时候将对象传进去
book.RegisterGoods("muzhaoy",20,19.9);
// RegisterGoods(&Book,"muzhaoy",20,19.9);
tea.RegisterGoods("shiqiany",30,18.9);
// RegisterGoods(&tea,"shiqiany",30,18.9);
book.countTotal();
// countTotal(&book);
tea.countTotal();
//countTotal(&tea);
所以,从逻辑上讲,是对象调用方法,从底层讲,其实将当前对象作为地址传递给成员方法。
如果给每一个对象分配成员方法,所占内存太大,所以采取上述方法。只耗损了一个this指针。四字节。
- 为了防止人为修改this,导致无法识别成员因此系统给this指针加了const
如下:
void RegisterGoods(Goods *const this,const char *name,int amout ,float price) ;
- 当对象调用成员函数的时候才有this指针,不调用时,this指针不存在,this指针存在于成员函数内部,且每次对对象操作完,this指针就会消失。
当函数调用结束,this指针就不存在了。 - 不是类的成员方法,全局函数按照普通函数编译,不会按照类的方法编译,给予this指针。外部函数一定没有this指针。
问题一:是不是类的成员函数一定要加this指针?
答:不是,
类中的静态成员函数,友元函数,内联函数没有this指针。
- 静态成员函数只能使用静态成员变量。静态成员变量不依赖类和this指针。
- 友元函数的定义在类外,不需要通过对象来访问。
- 如果是内联函数就不需要,传入this,因为会在调用点代码直接展开,根本不需要开辟栈帧,也就不需要this指针去指向开辟各自的私有成员属性的空间。
问题二:对象中有没有this指针?
答,没有,对象调用成员方法,是将对象的地址传给成员方法,从而达到每个对象独立的访问私有成员属性。
进一步了解是this指针:
- 在成员函数后面加const:
float GetTotal_value()const //后面加const,等价于下面。
float GetTotal_value(const Goods *const this)
{
int x= this -> Amout;
this -> Amout =1000; //是错的,因为this指针的指向作用被const限定了。
//*this 不能变
}
Goods c1,c2
//c1.GetTotal_value(); == GetTotal_value(&c1)
const Goods *const this,可以指向对象c1,但无法指向对象二。并且指向的值也不能改变。
同样的c2.GetTotal_value(); 也是如此。
- 常对象,常方法,和 普通对象,普通方法的关系
常对象只能调用常方法
如下:
class Coods
{
private:
char Name[21];
int Amout ;
float Price;
普通方法:
pubilc:
float Getprice() // float Getprice(Coods *const price){......}
{
return this->Price;
}
float Getprice(Coods *const price){......}
常方法:
float GetTotal_value(const Coods *const price)
float GetTotal_value()const //float GetTotal_value(const Coods *const price)
{
return Total_value;
}
};
Coods c1 = {"c++",2,12.3};
const Coods c2 = {"java",3,13.4};
c1. Getprice()
//普通函数可以调用普通方法
c1.GetTotal_value()
//普通函数可以调用常方法,能力被缩小。
c2. Getprice()
//常对象调用普通方法,不可以,能力被扩大。
可以理解为 :
int a = 10
const int *p = &a;
int *const s = p;//错误的!!!
c2. GetTotal_value()
//常对象调用常方法。
结论:常对象可以调用常方法,不可以调用普通方法
普通对象都能调。
静态成员变量:
- 无论构造多少个对象,一个类只有一份,
- 存储在数据区。
- 必须在类外的【.cpp文件中】进行初始化,切只能初始化一次。
- 静态成员变量可以不依赖对象,不依赖this指针。