文章目录
对象
C++ 中的类(Class)可以看做C语言中结构体(Struct)的升级版
类是一个通用的概念,C++、Python、C#、PHP 等很多编程语言中都支持类,都可以通过类创建对象
C++、Python、C#、PHP 等语言都支持类和对象,所以使用这些语言编写程序也被称为面向对象编程,这些语言也被称为面向对象的编程语言
C语言因为不支持类和对象的概念,被称为面向过程的编程语言
在C语言中,我们会把重复使用或具有某项功能的代码封装成一个函数,将拥有相关功能的多个函数放在一个源文件,再提供一个对应的头文件,这就是一个模块,使用模块时,引入对应的头文件就可以
在 C++ 中,多了一层封装,就是类(Class)。类由一组相关联的函数、变量组成,你可以将一个类或多个类放在一个源文件,使用时引入对应的类就可以
面向对象编程在代码执行效率上绝对没有任何优势,它的主要目的是方便程序员组织和管理代码,快速梳理编程思路,带来编程思想上的革新
面向对象编程是针对开发中大规模的程序而提出来的,目的是提高软件开发的效率
不要把面向对象和面向过程对立起来,面向对象和面向过程不是矛盾的,而是各有用途、互为补充的。如果你希望开发一个贪吃蛇游戏,类和对象或许是多余的,几个函数就可以搞定;但如果开发一款大型游戏,绝对离不开面向对象
============
类是创建对象的模板,一个类可以创建多个对象,每个对象都是类类型的一个变量
创建对象的过程也叫类的实例化,每个对象都是类的一个具体实例(Instance),拥有类的成员变量和成员函数
与结构体一样,类只是一种复杂数据类型的声明,不占用内存空间;
而对象是类这种数据类型的一个变量,或者说是通过类这种数据类型创建出来的一份实实在在的数据,所以占用内存空间
1、定义类
class Student{
public:
// 成员变量
char *name;
int age;
float score;
// 成员函数
void say(){
cout<<name<<"的年龄是"<<age<<",成绩是"<<score<<endl;
}
};
{ }内部是类所包含的成员变量和成员函数,它们统称为类的成员(Member)
在类定义的最后有一个分号 ; 它是类定义的一部分,表示类定义结束了,不能省略
class是 C++ 中新增的关键字,专门用来定义类
public也是 C++ 的新增关键字,它只能用在类的定义中,表示类的成员变量或成员函数具有“公开”的访问权限
类只是一个模板(Template),编译后不占用内存空间,所以在定义类时不能对成员变量进行初始化,因为没有地方存储数据。只有在创建对象以后才会给成员变量分配内存,这个时候就可以赋值了
2、创建对象
Student leijun; // 创建对象
class Student leijun; // 正确,class关键字可要可不要,但是出于习惯我们通常会省略掉 class 关键字
Student leijun; // 同样正确
除了创建单个对象,还可以创建对象数组:
Student stus[100];
该语句创建了一个 stus 数组,它拥有100个元素,每个元素都是 Student 类型的对象
3、 使用 . 访问成员
#include <iostream>
using namespace std;
// 类通常定义在函数外面
class Student {
public:
// 成员变量
char *name;
int age;
float score;
// 成员函数
void say() {
cout << name << "的年龄是" << age << ",成绩是" << score << endl;
}
};
int main() {
// 创建对象
Student boy;
// 通过.可以使用对象中的成员
boy.name = "小明";
boy.age = 15;
boy.score = 92.5f;
boy.say();
// 创建对象2
Student girl;
// 通过.可以使用对象2中的成员
girl.name = "菲菲";
girl.age = 18;
girl.score = 88.7f;
girl.say();
return 0;
}
4、使用 ->访问成员
C语言中经典的指针在 C++ 中仍然广泛使用,尤其是指向对象的指针
上面代码中创建的对象 boy在栈上分配内存,需要使用&获取它的地址,例如:
Student boy;
Student *pStu = &boy;
pStu 是一个指针,它指向 Student 类型的数据,也就是通过 Student 创建出来的对象
当然,你也可以在堆上创建对象,这个时候就需要使用前面讲到的new关键字,例如:
Student *pStu = new Student;
通过 new 创建出来的对象,它在堆上分配内存,没有名字,必须使用一个指针变量来接收这个指针,否则以后再也无法找到这个对象了,更没有办法使用它
栈内存是程序自动管理的
堆内存由程序员管理,对象使用完毕后需要用 delete 删除
#include <iostream>
using namespace std;
class Student {
public:
char *name;
int age;
float score;
void say() {
cout << name << "的年龄是" << age << ",成绩是" << score << endl;
}
};
int main() {
// 创建1个对象,让pStu这个指针变量指向它
Student *pStu = new Student;
// 既然pStu指向了一个对象(内存空间),那么就可以调用这个内存空间中的某个成员
pStu->name = "小明";
pStu->age = 15;
pStu->score = 92.5f;
// 当然也可以调用它里面的函数
pStu->say();
// 当不要pStu指向的对象时,需要用delete进行删除(就是释放那个内存空间)
delete pStu;//对不再使用的对象,记得用 delete 进行回收空间,这是一种良好的编程习惯
return 0;
}
代码运行visual studio 2022:
错误记录:不能将 “const char *“ 类型的值分配到 “char“ 类型的实体问题
解决方法:
在声明p指针指向char数组的时候 前面要加const 不然就会报错
5、this 指针
#include <iostream>
using namespace std;
class Student {
public:
char *name;
int age;
float score;
void say() {
//如果在成员函数中使用name,此时name没有在say函数中定义
//那么就会往前找这个name函数,此时发现找到了成员变量name
cout << name << "的年龄是" << age << ",成绩是" << score << endl;
}
void setNewAgeScore(int ageTemp, float scoreTemp) {
age = ageTemp;
score = scoreTemp;
}
void setNewAgeScore2(int age, float score) {
age = age;
score = score;
//在每个成员函数中直接使用,他是在编译的时候自动添加的,他指向了当前对象自己,类似python中的self
}
};
int main() {
// 创建一个对象
Student *pStu = new Student;
// 调用对象的成员变量
pStu->name = "小明";
pStu->age = 15;
pStu->score = 92.5f;
// 调用对象的成员函数
pStu->say();
// 设置新的年龄、分数
pStu->setNewAgeScore(16, 98.66f);
pStu->say();
pStu->setNewAgeScore2(17, 96.75);
pStu->say();
delete pStu; //删除对象
return 0;
}
上面代码结果:
为什么第3次的设置年龄与分数没有起作用呢?
void setNewAgeScore2(int age, float score) {
//首先,整体是一个赋值语句,会将=右侧限制性,即将age这个局部变量的值取出来,17,this->age=17
//既然=右侧已经计算完毕,那么就将这个结果,放到=左侧变量中
//=左侧 又是1个较复杂的计算,this->age,她先用this
//此时this指向一个对象
this->age = age;
this->score = score;
//在每个成员函数中直接使用,他是在编译的时候自动添加的,他指向了当前对象自己,类似python中的self
}
this 是 C++ 中的一个关键字,它指向当前对象,通过它可以访问当前对象的所有成员
说的直白一点:在成员函数被调用的过程中,会自动生成1个this指针变量,它指向当前调用对象自己,因此可以直接使用this->操作成员变量与成员函数
this 实际上是成员函数的一个形参,在调用成员函数时将对象的地址作为实参传递给 this。不过 this 这个形参是隐式的,它并不出现在代码中,而是在编译阶段由编译器默默地将它添加到参数列表中
地址关系:
#include <iostream>
using namespace std;
class Student {
public:
void printThis() {
cout << this << endl;
}
};
int main() {
Student *pStu1 = new Student;
pStu1->printThis();
cout << pStu1 << endl;
cout << "------------------\n";
Student *pStu2 = new Student;
pStu2->printThis();
cout << pStu2 << endl;
delete pStu1; //删除对象
delete pStu2; //删除对象
return 0;
}
PS:
记录问题
&pStu 和 *pStu 的地址是不一样的。
&pStu 表示的是指针 pStu 本身在内存中的地址。
*pStu 表示的是通过指针 pStu 所指向的 Student 类型的对象。
例如,如果 pStu 存储的地址是 0x1234 ,那么 &pStu 就是 pStu 这个指针变量在内存中的地址,比如 0x5678 ,而 *pStu 表示的是位于 0x1234 处的 Student 对象。
6、构造函数
构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行
怎样定义?
与类名同名,没有返回值,可以被重载
有什么用?
通常用来做初始化工作
7、析构函数
一种在对象销毁时,自动调用的函数
怎样定义?
析构函数名称与类名称相同,只是在前面加了个波浪号 ~ 作为前缀,它不会返回任何值,也不能带有任何参数,不能被重载
有什么用?
一般用于释放资源,例如关闭打开的文件,打开的网络socket套接字等
#include <iostream>
#include <string>
using namespace std;
class Student {
public :
char *name;
int age;
// 构造函数
Student() {
cout << "执行无参构造函数" << endl;
}
Student(char *name) {
cout << "执行含有一个参数的参构造函数" << endl;
}
Student(char *name, int age) {
cout << "执行含有两个参数的构造函数" << endl;
}
// 析构函数
~Student() {
cout << "执行析构函数" << endl;
}
};
int main() {
// 创建对象时,自动调用构造函数
Student *pStu1 = new Student;
Student *pStu2 = new Student;
Student *pStu3 = new Student;
// 销毁对象时,自动调用析构函数
delete pStu1;
delete pStu2;
delete pStu3;
return 0;
}
8、重载
函数名相同,形参不同,调用的时候会自动调对应的
案例:
目的:了解怎样让多个对象之间如何产生关联,从而实现发挥面向对象编程的威力
错误记录:
1没有与参数列表匹配的构造函数
构造函数里的参数定义要与成员变量一致
#include <iostream>
using namespace std;
class Gun {
public:
const char* name;
Gun(const char* name) { // 添加构造函数
this->name = name;
}
const char* get_name() {
return this->name;
}
void display_info() {
cout << "name:" << this->name << "\n";
}
};
class Bullet {
public:
int kill_power;
Bullet(int kill_power) {
this->kill_power = kill_power;
}
void display_info() {
cout << "子弹的威力值:" << this->kill_power << "\n";
}
};
class Person {
public:
const char *name;
Gun* gun;
Person(const char *name) {
this->name = name;
}
void display_info() {
cout << "name:" << this->name << "\n";
}
void get_gun(Gun* gun) {
this->gun = gun;
cout << this->name << " 拿起了" << gun->get_name() << "\n";
}
};
int main(){
class Person hero("钢铁侠");
hero.display_info();
class Gun gun("AK47");
gun.display_info();
hero.get_gun(&gun);
return 0;
}
#include <iostream>
using namespace std;
class Bullet {
public:
int kill_power;
Bullet(int kill_power) {
this->kill_power = kill_power;
}
void display_info() {
cout << "子弹的威力值:" << this->kill_power << "\n";
}
int get_power() {
return this->kill_power;
}
};
class Gun {
public:
const char* name;
Bullet* bullet;
Gun(const char* name) { // 添加构造函数
this->name = name;
}
const char* get_name() {
return this->name;
}
void display_info() {
cout << "name:" << this->name << "\n";
}
void install_bullet(Bullet* bullet) {
this->bullet = bullet;
cout << this->name << " 已经安装子弹\n";
}
};
class Person {
public:
const char *name;
Gun* gun;
Bullet* bullet;
Person(const char *name) {
this->name = name;
}
void display_info() {
cout << "name:" << this->name << "\n";
}
void get_gun(Gun* gun) {
this->gun = gun;
cout << this->name << " 拿起了" << gun->get_name() << "\n";
}
void get_bullet(Bullet *bullet) {
this->bullet = bullet;
cout << this->name << " 拿起了子弹,威力是:" << bullet->get_power() << "\n";
}
void install_bullet_to_gun() {
this->gun->install_bullet(this->bullet);
}
};
int main(){
class Person hero("钢铁侠");
hero.display_info();
class Gun gun("AK47");
gun.display_info();
class Bullet bullet(10);
bullet.display_info();
hero.get_gun(&gun);
hero.get_bullet(&bullet);
hero.install_bullet_to_gun();
return 0;
}
注意:
一开始Gun类的定义在Bullet类的定义之前,但是它使用了Bullet类。这会导致编译器无法识别Bullet类。我们需要提前声明Bullet类。
另外,确保所有指针变量在使用之前已经正确初始化。