C++快速理解之面向对象

对象

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类。
另外,确保所有指针变量在使用之前已经正确初始化。

  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

挥剑决浮云 -

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值