首页 > 编程语言 >【C++掌中宝】类和对象(一):类的定义和实例化

【C++掌中宝】类和对象(一):类的定义和实例化

时间:2024-09-26 11:21:23浏览次数:3  
标签:掌中宝 int 成员 C++ 对象 实例 函数 代码 变量

在这里插入图片描述

文章目录

引言

面向对象编程(OOP)是一种编程范式,它通过将现实世界中的事物抽象为对象来组织和管理代码。OOP的核心概念包括类、对象、封装、继承和多态。

面向对象编程(OOP)的概念及其重要性

  1. 概念
    • 对象:对象是现实世界中事物的抽象表示,包含了事物的属性和行为。在编程中,对象是一个具体的实例,拥有状态(属性)和行为(方法)。
    • :类是对象的模板或蓝图,定义了对象的属性和方法。类是创建对象的蓝图,描述了具有相同属性和行为的对象应当具备的特征。
    • 封装:封装是将对象的属性和方法隐藏在对象内部,只通过对象提供的接口与外界交互。这有助于保护对象的状态,防止外部直接访问和修改对象的私有属性。
    • 继承:继承是类与类之间的关系,它允许一个类继承另一个类的属性和方法。通过继承,可以实现代码的重用和扩展。
    • 多态:多态是指允许一个接口被多个类实现,或一个父类引用指向多个子类对象。这使得程序在运行时能够根据对象的实际类型来调用相应的方法,增加了程序的灵活性和可扩展性。
  2. 重要性
    • 模块化:OOP将代码组织成独立的对象,使得代码更加模块化,易于管理和维护。
    • 可重用性:通过继承和多态,OOP实现了代码的重用,减少了重复代码,提高了开发效率。
    • 可扩展性:OOP的结构使得代码易于扩展,可以在不修改现有代码的情况下添加新功能。
    • 易维护性:封装特性使得对象的内部实现细节对外部隐藏,减少了代码之间的耦合,提高了代码的可维护性。

在这里插入图片描述

类和对象是OOP的核心,因为它们提供了一个结构化的方式来组织和管理代码,使得代码更加模块化、可重用和易于维护。

1. 什么是类?

类是对象的模板或蓝图,定义了对象的属性和方法。类描述了一类对象的共同特征和行为。例如,一个“汽车”类可以包含属性如颜色、品牌、速度,以及方法如加速、减速等。

1.1 类的定义

在这里插入图片描述

1.1.1 类定义格式

  • class为定义类的关键字,Stack为类的名字(类名由自己决定,这里以下面例子为例),{}中为类的主体,注意类定义结束时后面分号不能省略。类体中内容称为类的成员:

    • 类中的变量称为类的属性或成员变量;
    • 类中的函数称为类的方法或者成员函数。
  • 为了区分成员变量,一般习惯上成员变量会加一个特殊标识,如成员变量前面或者后面加_ 或者m开头,注意C++中这个并不是强制的,只是一些惯例,具体看公司的要求。

  • C++中struct也可以定义类,C++兼容C中struct的用法,同时也将struct升级成了类,明显的变化是struct中可以定义函数,一般情况下我们还是推荐用class定义类。

  • 定义在类面的成员函数默认为inline但是最终要不要做内联,展开,还是看C++编译器自己去做决定)。

示例代码1

#include<iostream>
using namespace std;
class Stack
{
public:
	// 成员函数
	void Init(int n = 4)
	{
		array = (int*)malloc(sizeof(int) * n);
		if (nullptr == array)
		{
			perror("malloc申请空间失败");
			return;
		}
		capacity = n;
		top = 0;
	}
	void Push(int x)
	{
		// ...扩容
		array[top++] = x;
	}
	int Top()
	{
		assert(top > 0);
		return array[top - 1];
	}
	void Destroy()
	{
		free(array);
		array = nullptr;
		top = capacity = 0;
	}
private:
	// 成员变量
	int* array;
	size_t capacity;
	size_t top;
}; // 分号不能省略
int main()
{
	Stack st;
	st.Init();
	st.Push(1);
	st.Push(2);
	cout << st.Top() << endl;
	st.Destroy();
	return 0;
}

示例代码2

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	// 为了区分成员变量,一般习惯上成员变量
	// 会加一个特殊标识,如_ 或者 m开头
	int _year; // year_ m_year
	int _month;
	int _day;
};
int main()
{
	Date d;
	d.Init(2024, 3, 31);
	return 0;
}

示例代码3

#include<iostream>
using namespace std;
// C++升级struct升级成了类
// 1、类里面可以定义函数
// 2、struct名称就可以代表类型
// C++兼容C中struct的用法
typedef struct ListNodeC
{
	struct ListNodeC* next;
	int val;
}LTNode;
// 不再需要typedef,ListNodeCPP就可以代表类型
struct ListNodeCPP
{
	void Init(int x)
	{
		next = nullptr;
		val = x;
	}
	ListNodeCPP* next;
	int val;
};
int main()
{
	return 0;
}

1.1.2 访问限定符

  • C++一种实现封装的方式,用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

  • public修饰的成员在类外可以直接被访问;protectedprivate修饰的成员在类外不能直接被访问,protectedprivate是一样的,以后继承的地方才能体现出他们的区别。

  • 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止,如果后面没有访问限定符,作用域就到 }即类结束。

  • class定义成员没有被访问限定符修饰时默认为privatestruct默认为public

  • 一般成员变量都会被限制为private/protected,需要给别人使用的成员函数会放为public

在这里插入图片描述

1.1.3 类域

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

  • 类域影响的是编译的查找规则,下面程序中Init如果不指定类域Stack,那么编译器就把Init当成全局函数,那么编译时,找不到array等成员的声明/定义在哪里,就会报错。指定类域Stack,就是知道Init是成员函数,当前域找不到的array等成员,就会到类域中去查找

示例代码

#include<iostream>
using namespace std;
class Stack
{
public:
	// 成员函数
	void Init(int n = 4);
private:
	// 成员变量
	int* array;
	size_t capacity;
	size_t top;
};
// 声明和定义分离,需要指定类域
void Stack::Init(int n)
{
	array = (int*)malloc(sizeof(int) * n);
	if (nullptr == array)
	{
		perror("malloc申请空间失败");
		return;
	}
	capacity = n;
	top = 0;
}
int main()
{
	Stack st;
	st.Init();
	return 0;
}

1.2 类在编程中的作用——抽象与封装

  1. 抽象:抽象是将复杂的现实世界事物简化为编程中的模型。通过类,我们可以忽略对象的复杂细节,只关注其主要特征和行为。例如,汽车类可以抽象为具有属性(如颜色、品牌)和行为(如启动、停止)的模型。
  2. 封装:封装是将数据和操作数据的方法绑定在一起,并隐藏对象的内部实现细节。通过封装,类的内部状态只能通过特定的方法进行访问和修改,从而提高了代码的安全性和可维护性。

2. 类的基本组成

2.1 成员变量:类的属性(变量)

成员变量(也称为数据成员)是类的属性,用于描述类对象的状态。它们定义了对象拥有的特征。每个类对象会拥有自己的一组成员变量,并且这些成员变量可以在对象的生命周期内保存特定的数据。

成员变量可以是各种数据类型,例如intdoublestring,甚至可以是其他类的对象。通常,成员变量会被设置为privateprotected以实现封装,防止外部代码直接修改这些属性。

示例代码

class Person {
private:
    string name;  // 成员变量,存储人的名字
    int age;      // 成员变量,存储人的年龄
};

在这个例子中,nameagePerson类的成员变量,它们描述了Person对象的名字和年龄。

2.2 成员函数:类的行为(函数)

成员函数(也称为方法)定义了类的行为,它们操作对象的成员变量或执行某些功能。通过成员函数,外部代码可以访问和修改对象的成员变量,或执行与对象相关的操作。

成员函数通常设置为public,以便能够被外部代码调用。在成员函数中,可以使用类的成员变量,且不需要通过对象来访问这些变量。

示例代码

class Person {
private:
    string name;
    int age;

public:
    // 构造函数,用于初始化成员变量
    Person(string n, int a) : name(n), age(a) {}

    // 成员函数,用于输出对象的介绍
    void introduce() {
        cout << "Hi, my name is " << name << " and I am " << age << " years old." << endl;
    }
};

在这个例子中,introduce()Person类的成员函数,它可以访问成员变量nameage,并输出这些变量的值。

2.3 完整代码示例

以下是一个完整的代码示例,展示如何定义成员变量和成员函数,以及如何使用它们。

#include <iostream>
using namespace std;

class Person {
private:
    string name;  // 成员变量:人的名字
    int age;      // 成员变量:人的年龄

public:
    // 构造函数,用于初始化成员变量
    Person(string n, int a) : name(n), age(a) {}

    // 成员函数,用于设置名字
    void setName(string n) {
        name = n;
    }

    // 成员函数,用于获取名字
    string getName() {
        return name;
    }

    // 成员函数,用于设置年龄
    void setAge(int a) {
        age = a;
    }

    // 成员函数,用于获取年龄
    int getAge() {
        return age;
    }

    // 成员函数,用于介绍自己
    void introduce() {
        cout << "Hi, my name is " << name << " and I am " << age << " years old." << endl;
    }
};

int main() {
    // 创建对象并初始化成员变量
    Person person1("John", 30);

    // 调用成员函数
    person1.introduce();  // 输出 "Hi, my name is John and I am 30 years old."

    // 修改成员变量并输出
    person1.setName("Mike");
    person1.setAge(35);
    person1.introduce();  // 输出 "Hi, my name is Mike and I am 35 years old."

    return 0;
}

在这个代码示例中,Person类有两个成员变量nameage,以及多个成员函数,用于设置和获取名字与年龄,并输出对象的介绍。

3. 什么是对象?

对象:对象是类的实例。通过类创建的具体实例称为对象。每个对象都有自己的属性值和方法实现。例如,通过“汽车”类创建的具体汽车对象可以有不同的颜色和品牌。

2.1 实例化

2.1.1 实例化概念

  • 用类类型在物理内存中创建对象的过程,称为类实例化出对象。

  • 类是对象进行一种抽象描述,是一个模型一样的东西,限定了类有哪些成员变量,这些成员变量只是声明,没有分配空间,用类实例化出对象时,才会分配空间。

  • 一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储类成员变量。

    打个比方:类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,设计图规划了有多少个房间,房间大小功能等,但是并没有实体的建筑存在,也不能住人,用设计图修建出房子,房子才能住人。同样类就像设计图一样,不能存储数据,实例化出的对象分配物理内存存储数据。

在这里插入图片描述

#include<iostream>
using namespace std;
class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	// 这里只是声明,没有开空间
	int _year;
	int _month;
	int _day;
};
int main()
{
	// Date类实例化出对象d1和d2
	Date d1;
	Date d2;
	d1.Init(2024, 3, 31);
	d1.Print();
	d2.Init(2024, 7, 5);
	d2.Print();
	return 0;
}

2.1.2 对象大小(内存中的体现)

分析一下类对象中哪些成员呢?

类实例化出的每个对象,都有独立的数据空间,所以对象中肯定包含成员变量。

那么成员函数是否包含呢?

首先函数被编译后是一段指令,对象中没办法存储,这些指令存储在一个单独的区域(代码段),那么对象中非要存储的话,只能是成员函数的指针。

再分析一下,对象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各自独立的成员变量_year/_month/_day存储各自的数据,但是d1和d2的成员函数Init/Print指针却是一样的,存储在对象中就浪费了。如果用Date实例化100个对象,那么成员函数指针就重复存储100次,太浪费了。这里需要再额外哆嗦⼀下,其实函数指针是不需要存储的,函数指针是一个地址,调用函数被编译成汇编指令[call 地址], 其实编译器在编译链接时,就要找到函数的地址,不是在运行时找,只有动态多态是在运行时找,就需要存储函数地址,这个我们以后会讲解。

在这里插入图片描述

上面我们分析了对象中只存储成员变量,C++规定类实例化的对象也要符合内存对齐的规则。

标签:掌中宝,int,成员,C++,对象,实例,函数,代码,变量
From: https://blog.csdn.net/2301_80191662/article/details/142546434

相关文章

  • 【C++掌中宝】从std的角度来进一步了解命名空间
    文章目录前言1.什么是命名空间(namespace)?2.\<iostream\>和\<iostream.h\>的区别3.C++命名空间的三种使用方式3.1直接指定标识符3.2使用using关键字3.3使用usingnamespacestd4.为什么避免使用usingnamespacestd5.命名空间冲突与解决方案6.命名空间的最......
  • 【C++】线程池
    C++线程池1.什么是线程池?解决什么问题?C++线程池(ThreadPool)的出现主要是为了解决以下几个问题:性能:创建和销毁线程都是相对昂贵的操作,特别是在高并发场景下,频繁地创建和销毁线程会极大地降低程序的性能。通过线程池预先创建一定数量的线程并保存在内存中,可以避免频繁地创建和销......
  • 【C++】C++核心编程
    C++核心编程本阶段主要针对C++面向对象编程技术,C++中的核心和精髓。1.内存分区模型C++程序在执行时,将内存大方向分为4个区域:代码区:存放函数体的二进制代码,由操作系统进行管理全局区:存放全局变量和静态变量以及常量栈区:由编译器自动分配释放,存放函数的参数值,局部变量等堆......
  • 【C++】C++提高编程
    C++提高编程本阶段主要针对C++泛型编程和STL技术做详细讲解,探讨C++更深层的使用1.模板1.1模板的概念模板就是建立通用的模具,大大提高复用性模板的特点:模板不可以直接使用,只是一个框架模板的通用并不是万能的1.2函数模板C++另一种编程思想称为泛型编程,主要利用技术......
  • 【C++】C++基础知识
    C++基础1.指针1.1定义与使用指针在内存中占多少字节?指针在32位操作系统中占4个字节,在64位操作系统中占8个字节。定义指针的两种方式如下/***定义指针的两种形式*///1.inta=10;int*p;p=&a;//2.int*p2=&a;1.2空指针与野指针空指针空指针......
  • 广州C++信奥老师解一本通题 1260:1282:最大子矩阵
    ​ 【题目描述】已知矩阵的大小定义为矩阵中所有元素的和。给定一个矩阵,你的任务是找到最大的非空(大小至少是1×1)子矩阵。比如,如下4×4的矩阵0 -2-7 09 2-6 2-4 1-4 1-1 8 0-2 的最大子矩阵是92-41-18 这个子矩阵的大小是15......
  • 如何解决 :libstdc++.so.6: version `GLIBCXX_3.4.30‘ not found
    如何解决:libstdc++.so.6:version`GLIBCXX_3.4.30‘notfound问题描述:当您尝试在Linux系统上运行某个程序或软件时,有时会遇到一个错误,提示libstdc++.so.6:versionGLIBCXX_3.4.30notfound。这个错误表明您的系统缺少某个特定版本的C++标准库,具体来说就是GLIBCXX_3.4.30......
  • C++ 的异常安全理念
    C++中的异常安全是一个至关重要的概念,它关乎到程序的健壮性、资源管理和数据状态的一致性。以下是对C++异常安全的详细解析:一、异常安全的概念异常安全是指在程序面对函数或方法可能抛出异常的情况下,仍能保证资源的正确释放和数据状态的一致性。这要求程序在异常发生时,能够妥......
  • C++ day06
    手动实现栈:#include<iostream>#include<cstring>usingnamespacestd;classStatic{private:int*arr;//动态分配栈inttop;//指向栈顶元素intcapacity;//记录栈的最大容量public://有参构造函数Static(i......
  • C++ 修饰符类型
    C++中的类型限定符const实例volatile实例mutable实例static实例register实例C++允许在 char、int和double 数据类型前放置修饰符。修饰符是用于改变变量类型的行为的关键字,它更能满足各种情境的需求。下面列出了数据类型修饰符:signed:表示变量可以存储......