当前位置: 首页 > article >正文

C++基础_类的基本理解

文章目录

  • 1. 类的基本理解
  • 2. 组织代码并管理复杂性
    • 2.1 封装
      • 用类来封装数据和行为
      • 使用private封装数据
    • 2.2 减少重复代码,增强复用性
    • 2.3 提升代码的可扩展性 —— 继承与派生
    • 2.4 增强代码的逻辑性和可读性
    • 2.5 支持设计模式
      • 单例模式的应用
  • 3. 完整示例代码

本文暂不涉及关于类的语法规则,以介绍思路和为什么为主,并没有很详细地呈现C++语言规则本身相关内容,因此从语法角度来讲,示例中可能存在不合适的地方,但不影响整体的理解。

1. 类的基本理解

类是C++核心的编程方式。将数据以及操作这些数据的函数封装在一起,使得数据(“成员变量”)和函数(“成员方法”)都成为类的内部成员。这种封装机制能够让代码更加干净,更好地被理解和维护。
在这里插入图片描述
当程序规模增大、代码复杂性增加时,类的设计和使用尤为重要,能够让代码的组织结构更加清晰、逻辑更加集中,且能够管理复杂性。同时,也能够提高代码的复用性和扩展性。

每个类的对象,即类的“实例”,都会有自己的成员变量和成员方法。意味着每个对象可以拥有独立的状态和行为,保持模块化。

因此,可以简单理解为,类允许我们将相关的变量和功能打包到一个类型中,将数据和操作逻辑紧密结合起来,形成一个更具组织性、层次性的结构。

类并不会提供新的功能,可以简单总结:
用类可以搞定的事情,不用类一定也行的
但是如果用类搞不定的事情,不用类是肯定搞不定的

理论上所有用“类”可以实现的功能,都可以通过非面向对象的方式实现,所以C语言依然可用,但是类带来的优势却是非面向对象的方式难以替代的。

2. 组织代码并管理复杂性

2.1 封装

用类来封装数据和行为

假设一开始我们用全局变量来表示商品和客户信息,例如:

string productName;
double productPrice;
int productQuantity;

很快我们就会发现,管理多个商品时需要更多的变量,比如 productName1、productName2等。这样以来,变量数量增加,代码会变得混乱。

用类封装后,可以定义一个Product类,把商品的信息都封装在一起,这样,我们就可以通过创建多个Product对象来管理多个商品,而不用为每个商品创建单独的变量,代码结构更加清晰:

class Product
{
public:
	string m_name;
	double m_price;
	int m_quantity;
};

每个Product都有自己的信息,可以避免一堆散乱的无组织的变量,从而引发混乱

int main()
{
	Product apple;
	apple.m_name = "Apple";
	apple.m_price = 2.5;
	apple.m_quantity = 100;

	Product banana;
	banana.m_name = "Banana";
	banana.m_price = 1.5;
	banana.m_quantity = 150;
	return 0;	
}

使用private封装数据

通过访问控制符(privare、protected和public)控制对类内部成员的访问,增强封装效果。

  • public 公有成员,类外部可以直接访问
  • private 私有成员,仅类的内部可以直接访问,类的外部不可直接访问
  • protected 受保护成员,仅类的内部和子类可以访问

通过这种方式,类提供对外的公共接口public方法,隐藏内部的private数据。

外部只能通过public方法(接口)来访问和操作类的私有数据成员。这样外部访问数据通过你给定的方式来访问数据,就变得可控,避免了不正确的操作对数据的不合理的更改,保障了数据安全性和一致性。

同时,封装隐藏了数据的具体情况,使得外部使用数据不依赖于内部数据的具体结构和存储方式,即便我们在类的内部修改了数据存储结构等细节,也不影响外部的调用。

2.2 减少重复代码,增强复用性

随着功能增加,假设我们还要显示商品详情。可以在 Product 类中增加一个方法来显示商品信息,这样不必在每个地方重复写代码。

	void displayInfo()
	{
		cout << "Product: " << m_name << " , Price: " << m_price << " , Quantity: " << m_quantity << endl;
	}

简单地调用
apple.displayInfo() 来显示苹果的详细信息,banana.displayInfo()来显示香蕉的详细信息。

apple.displayInfo();
banana.displayInfo();

2.3 提升代码的可扩展性 —— 继承与派生

假设我们的商店增加了一个新的需求:销售打折商品。
那么我们可以在不修改 Product 类的情况下,通过继承来扩展功能
创建一个 DiscountProduct 类,继承自 Product:

class DiscountProduct : public Product
{
public:
	double m_discountRate; // 按生活中通常的计算方式:打 7 折 * 0.7 不是 * 0.3
	double getDiscountPrice()
	{
		return m_price *   m_discountRate;
	}
	void displayInfo()
	{
		cout << "Product: " << m_name << " , Original Price: " << m_price
			<< " , Discounted Price: " << getDiscountPrice()
			<< " , Quantity: " << m_quantity << endl;
	}
};

虽然 DiscountProductProduct 都表示商品,但折扣商品需要额外的属性和行为(折扣率和折后价格计算),这里就是通过“继承”来复用 Product 类的已有代码,并在 DiscountProduct 类中扩展新的功能。

通过这种设计,避免了在 Product 类中增加和普通商品无关的内容(如折扣率),让Product保持简单,同时通过DiscountProduct来专门处理打折商品的逻辑

DiscountProduct类中,我们添加了一个新的成员变量折扣率,重写了displayInfo方法

2.4 增强代码的逻辑性和可读性

随着系统的复杂度增加,各种各样的数据会变得难以管理,假设我们没有类,而是直接用数组或全局变量记录商品、客户和订单信息,代码会变得难以阅读和理解。

但是如果用类来组织逻辑,就可以把相关的逻辑封装在各自的类里,比如 Customer 类 和 Order 类。

class Order
{
public:
	int m_orderID;
	vector<Product> m_products;

	Order(int id) :m_orderID(id) {}

	void addProduct(const Product& product)
	{		m_products.push_back(product);	
	}

	double calculateTotalAmount()
	{
		double total = 0.0;
		for (const auto& product : m_products)
		{
			total += product.m_price * product.m_quantity;
		}
		return total;
	}

	void dispalyOrderInfo()
	{
		cout << "Order ID: " << m_orderID << endl;
		for (auto& product : m_products)
		{
			product.displayInfo();
		}
		cout << "Total Amount: " << calculateTotalAmount() << endl;
	}
};
class Customer
{
public:
	string m_name;
	string m_address;
	vector<Order> m_orders;
	void addOrder(Order order)
	{
		m_orders.push_back(order);
	}
};

这样将客户的信息和订单集中在 Customer 类里,便于阅读和维护。同时,修改或者查找某个信息时,只要在对应的类中寻找即可,逻辑更加集中。

2.5 支持设计模式

还以上面的场景为例,假设我们要添加一个库存管理系统,用于跟踪没中商品的库存数量,以便商品在被添加到订单时更新库存,在库存不足时禁止下单。这正是单例模式非常典型的应用场景,在在大型项目中非常有用

单例模式的应用

在一个复杂的系统中,类似库存、日志、配置等全局资源通常需要集中管理。

想象一下,假如在购物系统中,库存管理存在多个实例,那么不同部分访问不同的实例,就会导致库存数据的不一致。

因此,必须提供一个全局访问点,系统中的任何模块(例如 customer order等),都只能通过系统中唯一一个库存管理实例来更新库存,这样才能安全、统一地访问库存数据,从而管理商品库存,避免多个库存数据副本带来库存数据的混乱,保证数据的一致性。

3. 完整示例代码

主要包括类的创建(例如Product类和DiscountProduct类)、继承与多态(例如DiscountProduct继承自Product并重写了displayInfo()方法)、单例模式(例如InventoryManager只有一个实例存在,避免数据不一致)、类的组合(例如Order包含Product列表,Customer包含Order列表)

每个类都围绕不同的职责进行了划分,使得代码结构清晰,易于扩展和维护

#include<iostream>
#include<string>
#include<vector>
#include<unordered_map>
using namespace std;

class Product
{
public:
	string m_name;
	double m_price;
	int m_quantity;

	Product(string name, double price, int quantity)
		:m_name(name), m_price(price), m_quantity(quantity) {}

	void displayInfo() const
	{
		cout << "Product: " << m_name << " , Price: " << m_price 
			<< " , Quantity: " << m_quantity << endl;
	}
};
class DiscountProduct : public Product
{
public:
	double m_discountRate; // 按生活中通常计算方式:打 7 折 * 0.7 不是 * 0.3

	DiscountProduct(string name, double price, int quantity, double discountRate)
		:Product(name, price, quantity), m_discountRate(discountRate) {}

	double getDiscountPrice()
	{
		return m_price *   m_discountRate;
	}
	void displayInfo()
	{
		cout << "Product: " << m_name << " , Original Price: " << m_price
			<< " , Discounted Price: " << getDiscountPrice()
			<< " , Quantity: " << m_quantity << endl;
	}
};

class InventoryManager 
{
private:
	static InventoryManager* instance;
	unordered_map<string, int> stock;  // 跟踪每个商品库存

	// 私有构造函数,防止外部直接创建实例
	InventoryManager() {}

public:
	// 禁用复制构造函数和赋值操作
	InventoryManager(const InventoryManager&) = delete;
	InventoryManager& operator=(const InventoryManager&) = delete;

	// 获取单例实例
	static InventoryManager* getInstance()
	{
		if (instance == nullptr)
			instance = new InventoryManager();
		return instance;
	}
	
	// 增加库存
	void addStock(const string& productName, int quantity)
	{
		stock[productName] += quantity;
		cout << "Added " << quantity << " units of " << productName << " to the inventory. " << endl;
	}

	// 检查库存
	bool isInStock(const string& productName, int quantity)
	{
		return stock[productName] >= quantity;
	}

	// 减少库存
	void reduceStock(const string& productName, int quantity)
	{
		if (isInStock(productName, quantity))
		{
			stock[productName] -= quantity;
			cout<<"Reduced "<<quantity<<" units of "<<productName<<" from the inventory. " << endl;
		}
		else
		{
			cout << "Insufficient stock for " << productName << " ! " << endl;
		}
	}

	// 显示库存信息
	void dispalyStock() const
	{
		cout << "Current Inventory: " << endl;
		for (const auto& item : stock)
		{
			cout << "Product: " << item.first << " , Quantity: " << item.second << endl;
		}
	}
};
// 初始化静态成员
InventoryManager* InventoryManager::instance = nullptr;

class Order
{
public:
	int m_orderID;
	vector<Product> m_products;

	Order(int id) :m_orderID(id) {}

	void addProduct(const Product& product)
	{
		InventoryManager* inventory = InventoryManager::getInstance();

		// 检查库存,确保可以添加到订单里
		if (inventory->isInStock(product.m_name, product.m_quantity))
		{
			m_products.push_back(product);
			inventory->reduceStock(product.m_name, product.m_quantity); // 更新库存
		}
		else
		{
			cout << "Product " << product.m_name << " is out of stock!" << endl;
		}
	}

	double calculateTotalAmount() const
	{
		double total = 0.0;
		for (const auto& product : m_products)
		{
			total += product.m_price * product.m_quantity;
		}
		return total;
	}

	void  displayOrderInfo() const
	{
			cout << "Order ID: " << m_orderID << endl;
			for (auto& product : m_products)
			{
				product.displayInfo();
			}
			cout << "Total Amount: " << calculateTotalAmount() << endl;
	}
};
class Customer
{
public:
	string m_name;
	string m_address;
	vector<Order> m_orders;
	void addOrder(Order order)
	{
		m_orders.push_back(order);
	}
};
int main()
{
	InventoryManager* inventory = InventoryManager::getInstance();

	// 增加库存
	inventory->addStock("Laptop", 10);
	inventory->addStock("Headphones", 20);

	// 创建产品
	Product laptop("Laptop", 1000.0, 1);
	DiscountProduct headphones("Headphones", 100.0, 2, 0.8);

	// 创建订单
	Order order(101);
	order.addProduct(laptop); 
	order.addProduct(headphones);

	// 显示订单信息
	order.displayOrderInfo();

	// 显示库存信息
	inventory->dispalyStock();
	return 0;	
}

http://www.kler.cn/a/374892.html

相关文章:

  • vue中如何为不同功能设置不同的默认打印设置(设置不同的打印机)
  • 重工业数字化转型创新实践:某国家特大型钢铁企业如何快速落地基于实时数仓的数据分析平台
  • Golang | Leetcode Golang题解之第522题最长特殊序列II
  • 某大型建设集团有限公司信息化技术方案(250页WORD)
  • 快速生成高质量提示词,Image to Prompt 更高效
  • 2024-10-25 问AI: [AI面试题] 强化学习是如何工作
  • 『 Linux 』网络传输层 - TCP(二)
  • NLP算法工程师精进之路:顶会论文研读精华
  • Rust整合Elasticsearch
  • el-tree展开子节点后宽度没有撑开,溢出内容隐藏了,不显示横向滚动条
  • 使用LangChain控制大模型的输出——解析器Parser
  • 人工智能:塑造未来生活的强大力量
  • 计组-层次化存储结构
  • uniapp+vite配置环境变量
  • Docker | 将本地项目发布到阿里云的实现流程
  • 第3关:命题逻辑推理
  • TQ15EG开发板教程:fmcomms8两片ADRV9009同步采集测试
  • SpringBoot后端开发常用工具详细介绍——flyway数据库版本控制工具
  • MyBatisPlus 中 LambdaQueryWrapper使用
  • ffmpeg+vue2
  • C++STL详解(九)map和set的使用
  • 探索高效办公新利器 ——ONLYOFFICE
  • TON 区块链开发的深入概述#TON链开发#DAPP开发#交易平台#NFT#Gamefi链游
  • django校园兼职系统-计算机毕业设计源码95561
  • 启明创投与七牛云坚定看好云计算发展前景
  • Java爬虫:如何优雅地从1688获取商品详情