十四天学会C++之第五天:类的详细讨论

1. 友元函数和友元类

  • 什么是友元函数和友元类,它们的作用。
  • 如何声明和使用友元函数和友元类,访问类的私有成员。

友元函数(Friend Functions)

友元函数是一种特殊的函数,它被允许访问类的私有成员。这意味着即使成员是私有的,友元函数也能够直接访问它们,而不需要通过公有接口。这提供了更多的灵活性,允许外部函数与类密切合作。

示例-演示如何声明和使用友元函数:

#include <iostream>class MyClass {
private:int secretData;public:MyClass() : secretData(0) {}friend void FriendFunction(MyClass& obj); // 友元函数的声明};// 友元函数的定义
void FriendFunction(MyClass& obj) {obj.secretData = 42; // 可以访问私有成员
}int main() {MyClass myObj;FriendFunction(myObj); // 调用友元函数std::cout << myObj.secretData << std::endl; // 输出 42return 0;
}

示例中,FriendFunction 被声明为 MyClass 的友元函数,可以直接访问 secretData 私有成员。

友元类(Friend Classes)

友元类与友元函数类似,但它允许整个类成为另一个类的友元,而不仅仅是一个函数。这意味着友元类的所有成员都可以访问其他类的私有成员。

class MyClass {
private:int secretData;public:MyClass() : secretData(0) {}friend class FriendClass; // 友元类的声明};class FriendClass {
public:void AccessPrivateData(MyClass& obj) {obj.secretData = 42; // 可以访问私有成员}
};int main() {MyClass myObj;FriendClass friendObj;friendObj.AccessPrivateData(myObj); // 通过友元类访问私有成员std::cout << myObj.secretData << std::endl; // 输出 42return 0;
}

FriendClass 被声明为 MyClass 的友元类,因此它的成员函数可以访问 secretData 私有成员。

2. 拷贝构造函数

  • 介绍拷贝构造函数的概念。
  • 定义和使用拷贝构造函数,以处理对象的复制。

拷贝构造函数是C++中的一个特殊构造函数,用于创建一个对象的副本。当对象按值传递给函数、作为函数的返回值返回或者在初始化过程中需要复制时,拷贝构造函数会被自动调用。它用于确保对象的复制是正确的,包括成员变量的深拷贝。

示例-演示了如何定义和使用拷贝构造函数

#include <iostream>
#include <cstring>class MyString {
private:char* str;public:// 构造函数,用于创建字符串对象MyString(const char* s) {str = new char[strlen(s) + 1];strcpy(str, s);}// 拷贝构造函数,用于创建对象的副本MyString(const MyString& other) {str = new char[strlen(other.str) + 1];strcpy(str, other.str);}// 析构函数,用于释放内存~MyString() {delete[] str;}// 显示字符串内容void display() {std::cout << str << std::endl;}
};int main() {MyString original("Hello, World!");MyString copy = original; // 使用拷贝构造函数创建副本original.display(); // 输出 "Hello, World!"copy.display(); // 输出 "Hello, World!"return 0;
}

在示例中,首先定义了一个 MyString 类,它包含一个字符数组 str 用于存储字符串。然后,定义一个拷贝构造函数,通过分配新内存并复制原始对象的内容来创建副本。最后,在 main 函数中,创建了一个 original 对象,并使用拷贝构造函数创建了 copy 对象。这两个对象分别存储相同的字符串内容,但它们在内存中有不同的副本。

3. 运算符重载

  • 解释运算符重载的概念。
  • 提供示例,说明如何重载常见的运算符,如+、-、*等。

运算符重载是C++中一种强大的特性,它允许为自定义类创建特定的运算符行为。通过运算符重载,可以让对象像内置类型一样执行加法、减法、乘法等操作,使代码更直观和易读。

示例-演示如何重载加法运算符:

#include <iostream>class Complex {
private:double real;double imag;public:Complex(double r, double i) : real(r), imag(i) {}// 运算符重载:重载+运算符,实现复数相加Complex operator+(const Complex& other) const {return Complex(real + other.real, imag + other.imag);}// 显示复数void display() const {std::cout << real << " + " << imag << "i" << std::endl;}
};int main() {Complex num1(3.0, 2.0);Complex num2(1.5, 4.5);Complex result = num1 + num2; // 使用重载的+运算符num1.display(); // 输出 "3 + 2i"num2.display(); // 输出 "1.5 + 4.5i"result.display(); // 输出 "4.5 + 6.5i"return 0;
}

在示例中,定义一个 Complex 类表示复数。然后,重载加法运算符 +,使得两个 Complex 对象可以像内置数值类型一样相加。通过运算符重载,让复数对象的操作更自然和直观。

4. 静态成员和静态函数

  • 讲解静态成员和静态函数的作用。
  • 如何声明和使用静态成员和静态函数。

在C++中,静态成员和静态函数是属于整个类而不是类的实例的。它们被称为类级别的成员,与类的每个实例无关,而是与类本身关联。

静态成员是在类级别共享的数据成员。它们对于所有类的实例都是相同的。要声明静态成员,可以使用 static 关键字。

#include <iostream>class MyClass {
public:static int count; // 静态成员变量MyClass() {count++; // 每次创建实例时增加计数}static void showCount() {std::cout << "Total instances: " << count << std::endl;}
};int MyClass::count = 0; // 初始化静态成员变量int main() {MyClass obj1;MyClass obj2;MyClass obj3;MyClass::showCount(); // 调用静态函数显示计数return 0;
}

我们创建一个名为 MyClass 的类,包含一个静态整数 count 用于跟踪创建的实例数。创建 MyClass 的实例时,静态成员变量 count 都会增加。定义一个静态函数 showCount 来显示实例的总数。

静态函数是在类级别共享的成员函数。它们不需要访问类的实例数据,因此可以在没有实例的情况下调用。静态函数使用与类相关的方式调用,而不是使用实例。

5. 类的继承和多态性

  • 介绍类的继承的概念,包括基类和派生类。
  • 讲解多态性的概念和实现方式,包括虚函数和运行时多态性。

在C++中,类的继承和多态性是面向对象编程的核心概念之一。它们允许构建更强大、更灵活的对象模型。

类的继承允许创建一个新的类(称为派生类),它可以继承另一个类(称为基类)的属性和行为。可以在现有类的基础上创建新类,而不必从头开始编写代码。派生类可以添加额外的成员变量和成员函数,也可以覆盖基类的成员函数以改变其行为。

#include <iostream>// 基类
class Shape {
public:virtual void draw() {std::cout << "绘制形状" << std::endl;}
};// 派生类
class Circle : public Shape {
public:void draw() override {std::cout << "绘制圆形" << std::endl;}
};int main() {Shape shape;Circle circle;shape.draw();  // 输出:绘制形状circle.draw(); // 输出:绘制圆形return 0;
}

基类 Shape 和一个派生类 Circle。派生类继承了基类的 draw 函数,并覆盖了它以提供不同的行为。在 main 函数中,我们创建了基类和派生类的对象,然后调用它们的 draw 函数,演示了多态性的概念。

多态性是一种能够在运行时选择正确函数版本的机制。在上面的示例中,Shape 类的 draw 函数是虚函数,而 Circle 类中的 draw 函数使用了 override 关键字来表示它是一个覆盖了基类函数的虚函数。这允许我们在基类指针或引用的上下文中调用派生类的函数,而选择的是正确的版本。

6. 示例和练习

  • 示例代码,演示友元函数、拷贝构造函数、运算符重载、静态成员、类的继承和多态性的用法。
  • 练习,以加强对这些高级主题的理解和应用。

1. 创建一个类 Fraction 表示分数,包括分子和分母。编写运算符重载函数,实现分数的加法和减法运算:

#include <iostream>class Fraction {
private:int numerator;int denominator;public:Fraction(int num, int den) : numerator(num), denominator(den) {// Ensure denominator is not zeroif (denominator == 0) {std::cerr << "Error: Denominator cannot be zero." << std::endl;exit(1);}}// Overload the + operator to add fractionsFraction operator+(const Fraction& other) const {int newNumerator = numerator * other.denominator + other.numerator * denominator;int newDenominator = denominator * other.denominator;return Fraction(newNumerator, newDenominator);}// Overload the - operator to subtract fractionsFraction operator-(const Fraction& other) const {int newNumerator = numerator * other.denominator - other.numerator * denominator;int newDenominator = denominator * other.denominator;return Fraction(newNumerator, newDenominator);}void display() const {std::cout << numerator << "/" << denominator << std::endl;}
};int main() {Fraction frac1(1, 2);Fraction frac2(1, 3);Fraction sum = frac1 + frac2;Fraction diff = frac1 - frac2;std::cout << "Fraction 1: ";frac1.display();std::cout << "Fraction 2: ";frac2.display();std::cout << "Sum: ";sum.display();std::cout << "Difference: ";diff.display();return 0;
}

解答说明:

  • 创建名为 Fraction 的类,表示分数,包括分子和分母属性。
  • 通过运算符重载,重载 +- 运算符,实现了分数的加法和减法。
  • main() 函数中,创建两个分数对象 frac1frac2,然后对它们进行加法和减法运算。

2. 创建一个基类 Vehicle 表示交通工具,包括名称和速度属性。创建两个派生类 CarBike,它们继承了基类并添加了特定的属性:

#include <iostream>
#include <string>class Vehicle {
protected:std::string name;double speed;public:Vehicle(const std::string& n, double s) : name(n), speed(s) {}void display() const {std::cout << "Name: " << name << ", Speed: " << speed << " km/h" << std::endl;}
};class Car : public Vehicle {
private:int numWheels;public:Car(const std::string& n, double s, int wheels) : Vehicle(n, s), numWheels(wheels) {}void display() const {std::cout << "Car - ";Vehicle::display();std::cout << "Wheels: " << numWheels << std::endl;}
};class Bike : public Vehicle {
private:bool hasBasket;public:Bike(const std::string& n, double s, bool basket) : Vehicle(n, s), hasBasket(basket) {}void display() const {std::cout << "Bike - ";Vehicle::display();std::cout << "Basket: " << (hasBasket ? "Yes" : "No") << std::endl;}
};int main() {Car car("Sedan", 120.0, 4);Bike bike("Mountain Bike", 25.0, true);car.display();bike.display();return 0;
}

解答说明:

  • 创建一个基类 Vehicle,包括名称和速度属性。
  • 然后,创建两个派生类 CarBike,它们继承了基类 Vehicle 并添加了特定的属性。
  • main() 函数中,创建了一个 Car 对象和一个 Bike 对象,分别调用它们的 display() 方法以显示车辆信息,包括名称、速度等。

3. 使用多态性存储和调用不同类型的动物对象:

#include <iostream>
#include <vector>class Animal {
public:virtual void speak() const {std::cout << "Animal speaks." << std::endl;}
};class Dog : public Animal {
public:void speak() const override {std::cout << "Dog barks." << std::endl;}
};class Cat : public Animal {
public:void speak() const override {std::cout << "Cat meows." << std::endl;}
};int main() {std::vector<Animal*> animals;animals.push_back(new Dog());animals.push_back(new Cat());for (const auto& animal : animals) {animal->speak();delete animal; // Don't forget to free memory}return 0;
}

解答说明:

  • 定义一个基类 Animal,创建两个派生类 DogCat
  • 在派生类中重写了虚函数 speak() 以实现不同类型的动物的声音。
  • 使用指向基类的指针存储不同类型的动物对象,实现了多态性。
  • 在循环中,通过基类指针调用虚函数 speak(),实际执行的是派生类的版本。

4. 银行账户类 BankAccount 和友元函数 transfer

#include <iostream>class BankAccount; // Forward declarationclass BankAccount {
private:std::string accountNumber;double balance;public:BankAccount(const std::string& accNum, double initBalance) : accountNumber(accNum), balance(initBalance) {}void displayBalance() const {std::cout << "Account: " << accountNumber << ", Balance: " << balance << " USD" << std::endl;}friend void transfer(BankAccount& from, BankAccount& to, double amount);
};void transfer(BankAccount& from, BankAccount& to, double amount) {if (from.balance >= amount) {from.balance -= amount;to.balance += amount;std::cout << "Transfer successful." << std::endl;} else {std::cout << "Insufficient balance for transfer." << std::endl;}
}int main() {BankAccount acc1("12345", 1000.0);BankAccount acc2("67890", 500.0);acc1.displayBalance();acc2.displayBalance();transfer(acc1, acc2, 300.0); // Transfer money from acc1 to acc2acc1.displayBalance();acc2.displayBalance();return 0;
}

解答说明:

  • 定义一个银行账户类 BankAccount,包括账户号码和余额属性。
  • 使用友元函数 transfer 实现了账户之间的资金转移。
  • main() 中,创建两个账户,显示它们的余额,然后进行转账操作。

5. 图形类 Shape 和派生类 CircleRectangle,计算总面积:

#include <iostream>
#include <vector>class Shape {
protected:std::string color;public:Shape(const std::string& c) : color(c) {}virtual double getArea() const {return 0.0; // Default area for a generic shape}
};class Circle : public Shape {
private:double radius;public:Circle(const std::string& c, double r) : Shape(c), radius(r) {}double getArea() const override {return 3.14159 * radius * radius; // Area of a circle}
};class Rectangle : public Shape {
private:double width;double height;public:Rectangle(const std::string& c, double w, double h) : Shape(c), width(w), height(h) {}double getArea() const override {return width * height; // Area of a rectangle}
};int main() {std::vector<Shape*> shapes;shapes.push_back(new Circle("Red", 5.0));shapes.push_back(new Rectangle("Blue", 4.0, 6.0));double totalArea = 0.0;for (const auto& shape : shapes) {totalArea += shape->getArea();delete shape; // Don't forget to free memory}std::cout << "Total Area: " << totalArea << std::endl;return 0;
}

解答说明:

  • 定义一个基类 Shape,创建两个派生类 CircleRectangle,重写虚函数 getArea() 以返回不同形状的面积。
  • 使用指向基类的指针存储不同类型的图形对象,实现了多态性。
  • 在循环中,计算了所有图形的总面积。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/114506.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

为什么要做字节对齐 alignment?

下面这段 C 代码的输出是什么&#xff1f;定义的 Type 占用的字节数&#xff08;下面简称为字节数&#xff09;是多少呢&#xff1f; #include <iostream>struct Type {char a;int b; };int main(void) {std::cout << sizeof(Type) << \n; }经过编译运行&am…

阿里面试(持续更新)

一面&#xff1a; 1 HashMap 实现原理&#xff0c;ConcurrentHashMap 实现原理 HashMap和ConcurrentHashMap都是存储键值对的数据结构&#xff0c;不同的是HashMap是线程不安全的&#xff0c;ConcurrentHashMap是线程安全的&#xff0c;HashMap在高并发情况下会出现数据不一致…

企业知识库软件,快速构建企业知识分享与团队协同的软件

企业知识库是一种特殊的在线协同文档工具&#xff0c;支持包括FAQ、文档、视频、知识图谱等。从本质上讲&#xff0c;它是基于企业知识库软件从而实现内部或外部知识的沉淀、集合、更新、共享等&#xff0c;能为员工或客户提供常见问题的标准回答。 今天我就基于HelpLook &…

敏捷是怎么提高工作效率的

敏捷管理是一门极力减少不必要工作量的艺术。 谷歌、亚马逊、苹果、微信、京东等全球 500 强企业都在用的管理方法&#xff0c;适用于各行各业&#xff0c;被盛赞为应获“管理学的诺贝尔奖”。 它专注于让员工不受种种杂事的羁绊&#xff0c;激发个体斗志&#xff0c;释放出巨大…

阶段六-Day05-MyBatis3

一、多表查询&#xff08;面试题&#xff09; 1. 介绍 多表查询是在企业中必不可少的&#xff0c;无论多么简单的项目里通常会出现多表查询的操作。因为只要是关系型数据库&#xff0c;在设计表时都需要按照范式进行设计&#xff0c;为了减少数据冗余&#xff0c;都会拆成多个…

Proteus仿真--量程自动切换数字电压表(仿真+程序)

本文主要介绍基于51单片机的量程自动切换数字电压表Proteus仿真设计&#xff08;完整仿真源文件及代码见文末链接&#xff09; 简介 硬件电路主要分为单片机主控模块、AD转换模块、量程选择模块以及数码管显示模块 &#xff08;1&#xff09;单片机主控模块&#xff1a;单片…

Golang开发软件

1. 引言 Go&#xff08;也称为Golang&#xff09;是一种开源的编程语言&#xff0c;由Google在2007年启动的项目中开发而来。它是一种静态类型的编译型语言&#xff0c;旨在提供高效、可靠的性能。相比于其他编程语言&#xff0c;Golang具有更高的执行效率和并发能力&#xff…

OpenStack云计算平台实战-----创建空白虚拟机

1、创建空白虚拟机 需要注意的步骤会截图一下&#xff0c;其它的基本都是下一步&#xff0c;默认的即可 建议将虚拟机命名为自己的名字加后缀 将处理器数量和每个处理器的内核量都修改为2 将虚拟机的内存设置为8G&#xff0c;不然不够用 将指定磁盘大小设置为200G&#xff0c;…

精讲stable diffusion的controlNet插件

controlNet插件是stable diffusion的一个重要插件&#xff0c;甚至可以说正是因为有了controlNet插件&#xff0c;stable diffusion才会具有midjourney所不具备的独特魅力&#xff01; 我们今天就一起来学习下controlNet插件的安装和每个模型的用法 插件主页 独立的controlN…

微信小程序授权登录介绍

目录 一. 小程序登录如何获取微信用户信息 二. 小程序微信授权登录示例 后台代码 小程序代码 效果展示 三. 微信emoji存储问题 一. 小程序登录如何获取微信用户信息 小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识&#xff0c;快速建立小程序内…

【Java 进阶篇】手把手教你创建 Bootstrap 旅游网站

随着互联网的普及&#xff0c;旅游行业在全球范围内迅速发展。人们通过网络规划、预订和分享他们的旅行经历。因此&#xff0c;拥有一个令人印象深刻的旅游网站对于吸引游客和提供有用信息至关重要。在本篇博客中&#xff0c;我们将手把手教您如何创建一个令人兴奋的旅游网站&a…

华为eNSP配置专题-OSPF路由协议的配置

文章目录 华为eNSP配置专题-OSPF路由协议的配置0、概要介绍1、前置环境1.1、宿主机1.2、eNSP模拟器 2、基本环境搭建2.1、终端构成和连接2.2、终端的基本配置 3、OSPF路由的配置3.1、OSPF路由的配置3.1.1、在R1上配置OSPF3.1.2、在R2和R3上配置OSPF3.1.3、查看和监控OSPF 华为e…

[传智杯 #5 初赛] I-不散的宴会

洛谷P8877 [传智杯 #5 初赛] I-不散的宴会 题目大意 学生社会可以被看作一个排列成等腰直角三角形的节点阵列。该节点阵列共有 n n n行&#xff0c;第 i i i行共有 i i i个节点&#xff0c;我们将第 i i i行第 j j j列的节点标号为 ( i , j ) (i,j) (i,j)。 这些点具有权值。…

正则表达式之学习笔记

正则表达式学习笔记 一、概念二、正则表达式组成三、常见的正则表达式3.1 .匹配任意字符3.2 * 匹配前一个字符的0个或多个实例3.3 ^ 匹配输入字符串的开头3.4 $ 匹配行尾3.5 [] 匹配字符集合\<\> 精确匹配符号 一、概念 正则表达式是由一系列特殊字符组成的字符串&#…

【笔试题】华为研发工程师编程题

1.汽水瓶 某商店规定&#xff1a;三个空汽水瓶可以换一瓶汽水&#xff0c;允许向老板借空汽水瓶&#xff08;但是必须要归还&#xff09;。 小张手上有n个空汽水瓶&#xff0c;她想知道自己最多可以喝到多少瓶汽水。 数据范围&#xff1a;输入的正整数满足 1≤n≤100 1≤n≤…

localforage-本地存储的优化方案

前言 前端本地化存储算是一个老生常谈的话题了&#xff0c;我们对于 cookies、Web Storage&#xff08;sessionStorage、localStorage&#xff09;的使用已经非常熟悉&#xff0c;在面试与实际操作之中也会经常遇到相关的问题&#xff0c;但这些本地化存储的方式还存在一些缺陷…

让uniGUI支持https

今天在专家的帮助下&#xff0c;成功的让uniGUI支持https了。 首先&#xff0c;去申请个**的证书。我同事去阿里申请的&#xff0c;申请回是一个zip文件&#xff0c;里面有两个文件&#xff0c;一个扩展是per&#xff0c;一个key 然后&#xff0c;把这两个证书文件放到uniGUI…

通过Chain Prompts方式将LLM的能力引入测试平台:正交实验测试用例生成

通过Chain Prompts方式将LLM的能力引入测试平台:正交实验测试用例生成 Chain Prompts Chain Prompts是指在一个对话或文本生成任务中,将前一个提示的输出作为下一个提示的输入,形成一个连续的链条。这种方法常常用于创建连贯的、有上下文关联的文本。在对话系统中,这种方…

微信小程序数据存储方式有哪些

在微信小程序中&#xff0c;数据存储方式有以下几种&#xff1a; 本地存储 本地存储是一种轻量级的数据存储方式&#xff0c;用于存储小量的数据&#xff0c;例如用户的配置信息、页面的状态等。微信小程序提供了 wx.setStorage() 和 wx.getStorage() 方法&#xff0c;用于将数…

【Java基础面试三十六】、遇到过异常吗,如何处理?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;遇到过异常吗&#xff0…