C++面向对象(oop) 如何用oop编程
(1)如何用OOP编程?
OOP语言的四大特征是什么?抽象 封装(隐藏) 继承 多态
怎么体现封装(隐藏):通过类的访问限定符:public 共有的 private 私有的 protected 保护的
在C中:定义各种各样的函数,struct可以自定义类型
在C++中:输出各种各样的类。类(给用户自定义类型的,描述为实体的抽象类型)在计算机世界里面的类来代表现实世界的实体的抽象类型。
用OOP的思想解决问题:
1、先找实体;
2、然后根据下面这个思维图,编写代码
ADT表示实体的抽象数据类型,分析出实体它有什么属性什么行为
在计算机的类里面,从实体属性得到的东西是类的成员变量;从实体行为得到的东西是类的成员方法。类并不代表实体,而是实体的ADT。
当我用这个类型去实例化对象后(对象是占栈内存空间的,就类似实体在现实世界里面占据空间是一样的), 对象在逻辑意义上来代表现实世界中的实体
注意:
计算机语言也讲究语言美,在命名类时统一采取,驼峰式命名;
调用成员方法时,C语言直接调用就好show(),C++还要加对象名good.show();
类型不占空间,用内存定义了对象才占用(栈)空间
const int NAME_LEN = 20;
//
class Goods //=》商品数据化类型,类型不占空间,用内存定义了对象才占用(栈)空间
{
public://给外部提供共有的方法,来访问实体私有的属性
void init(const char *name, double price, int amount);
//打印商品信息
void show();
//定义类成员方法,可以在类体内;处理时,自动处理成inline内联函数(复习)
//给成员变量提供一组getXXX或setXXX的方法,使我们在外部调用这些接口后,就可以对相应的成员变量进行赋值。
void setName(char *name) { strcpy(_name, name); }
void setPrice(double price) { _price = price; }
void setAmount(int amount) { _amount = amount; }
//char *getName();//定义char* 接口,返回普通的char*类型(即,传回了私有成员具体某一个值的地址),导致外部可以直接修改内部成员的值;修改方法:前面加const
const char *getName() { return _name; }
double getPrice() { return _price; }
int getAmount() { return _amount; }
private://属性一般都是私有的,不可访问
char _name[NAME_LEN];
double _price;
int _amount;
};
//定义类成员方法,也可以在类外,但是要加上作用域,不然就称为全局方法了。
void Goods::init(const char *name, double price, int amount)//想让它也称为inline内联函数,就要在前面加inline;
{
strcpy(_name, name);
_price = price;
_amount = amount;
}
void Goods::show()
{
cout << "name:" << _name << endl;
cout << "price:" << _price << endl;
cout << "amount:" << _amount << endl;
}
int main()
{
Goods good;//类实例化了一个对象
//cout << good._price << endl;//不能访问成员Coods::_price
good.init("面包", 10.0, 20);//在新版vs编译器里面,常量字符串不允许用普通指针来接收,要用常指针(即,加const)
good.show();
system("pause");
return 0;
}
char* name 为什么要加const?
void init(const char *name, double price, int amount);
因为在主函数中,在新版vs编译器里面,常量字符串不允许用普通指针来接收,要用常指针(即,加const)
good.init("面包", 10.0, 20);//在新版vs编译器里面,常量字符串不允许用普通指针来接收,要用常指针(即,加const)
定义类成员方法,也可以在类外,但是定义在域外,要加上作用域,不然就称为全局方法了;如果定义在域外,想让它也称为inline内联函数,就要在前面加inline,域内的成员方法默认为inline内联函数。
void Goods::init(const char *name, double price, int amount)
在主函数中,因为_pirce 是类的私有成员,所以不能直接访问。
cout << good._price << endl;//不能访问成员Coods::_price
如何计算对象的内存大小?
类似结构体的计算方式,对象的内存大小只依赖于成员变量,与成员方法的多少无关。算成员变量的大小是和结构体变量的内存计算方式一样:先找占用内存最长的成员变量,以其为内存字节对齐的方式。然后计算出总的对象的大小。
另一种方法:
在VS编译器里面 工具–>VS命令提示(不是win 的cmd)
1 打开所在的文件夹(当前想查看的类所在的路径)
2 然后cd + 路径名 打开这个路径:
3 输入dir:
4 输入cl cpp文件全称 /d1reportSingleClassLayout类名称
这个CGoods类的大小是40字节, 先是name 20字节。然后补4字节空白:最长的成员分量就是这
个double 8个字节(对象在排列的时候是以8字节对齐的)。一个char一个字节,所以为了补齐24字节(3*8=24 20+4=24);然后price8个字节,最后amount也是8个字节(4+4=8),所以最后就是(20+4+8+4+4)这两个加粗的4补齐加的空白,即40字节。
先看最长的成员分量占多少字节,以它为内存字节的对齐方式。不够的补齐。这个补位的目的:为了让内存字节对齐。字节对齐的最大优点:节省CPU在内存上访问数据的IO次数。(这个会在后面njax内存池上讲 内存对齐的东西)
注: 一个类可以定义无数个对象(不同的对象代表不同的实体),每一个对象都有自己的成员变量,但是他们共享一套成员方法(方法编译成指令是放在text段上的)。修改一个对象的成员变量不影响其他对象的成员变量。
当执行到第四步是可能会出现错误:
text.cpp c1xx: fatal error C1007: 无法识别的标志“-reporSingleClassLayoutGoods”(在“c1xx”中)
这个可能是你在安装vs的过程中,少了部分插件,点击“工具”——“获取工具或功能”,然后在“扩展开发”打钩,最后安装,这样就可以正常使用了。
this指针
问题:当用对象调用类里面的同一套方法时,这个方法是怎么知道处理哪个对象的信息呢?
//初始化
void init(const char *name, double price, int amount);//;怎么知道要给哪个对象初始化?
//打印商品信息
void show();//怎么知道处理哪个对象的信息?
为什么要this指针?
因为类可以定义无数个对象(不同的对象代表不同的实体),每一个对象都有自己的成员变量(他们之间互不冲突),但是他们共享一套成员方法(方法编译成指令是放在text段上的)。那么这一套的成员方法是如何区分对象的呢?这就是理解this指针的核心。
good.init("面包", 10.0, 20);
在调用函数时,加上对象名,相当于把调用这个方法的对象的地址给传进来了。这里和C没有什么区别:因为在汇编上只有函数调用,call指令等等。(汇编是不存在什么OOP的)对象调用成员方法,转变成了C的调用方式:把调用这个方法的对象的地址给传进来。
既然你传进去一个实参(地址实参),那么你就需要添加一个形参(this指针)来接收。good1是Goods类型, **所以当编译器在给类的成员方法一编译以后,会给所有的成员方法 参数 都加上一个this 指针 来接收调用这个方法的对象的地址。**但是我们可以不用写这个。
那么这一套的成员方法是如何区分对象的呢?
因为当用对象调用这些方法的时候,把调用这个方法的对象的地址通过实参给传给这些函数,这些函数就会使用一个在编译器在给类的成员方法一编译以后,会给所有的成员方法 参数 都加上一个this 指针 来接收调用这个方法的对象的地址。
在编译过程中,编译器会给成员方法 成员变量自动加上一个 this指向。(可以不加)
this指针的作用:在成员方法里面去区分当前类的不同对象,以处理不同对象的数据。