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

这篇具有很好参考价值的文章主要介绍了十四天学会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; // 输出 42
    return 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; // 输出 42
    return 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 zero
        if (denominator == 0) {
            std::cerr << "Error: Denominator cannot be zero." << std::endl;
            exit(1);
        }
    }

    // Overload the + operator to add fractions
    Fraction 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 fractions
    Fraction 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 declaration

class 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 acc2

    acc1.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;
}

解答说明:文章来源地址https://www.toymoban.com/news/detail-722063.html

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

到了这里,关于十四天学会C++之第五天:类的详细讨论的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 学习Android的第十四天

    目录 Android DatePicker 日期选择器 DatePicker 属性 和 事件 DatePicker 事件 获得 DatePicker 的值 Android TimePicker 时间选择器 TimePicker 属性 TimePicker 事件 获得 TimePicker 的值 Android CalendarView 日历视图 CalendarView 属性 CalendarView 事件 获得 CalendarView 的值 在Android中,DatePicker是一个用户界面组件

    2024年02月21日
    浏览(49)
  • 【力扣刷题 | 第二十四天】

    目录 前言: 1049. 最后一块石头的重量 II - 力扣(LeetCode) 494. 目标和 - 力扣(LeetCode) 总结:                  今天我们依然暴打动态规划 有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合,从中选出任意两块石头,然后将它们一起粉

    2024年02月14日
    浏览(38)
  • 算法练习第六十四天

    LCR 184. 设计自助结算系统 - 力扣(LeetCode) 总结:利用一个双端维护队列一个往后递减的队列,对头是最大值,每次进入一个新值时就一直和队尾元素比较将比新的值小的数排出,这样能保证留在队列中的数都是会对最大值产生影响的数,而当主队列中将要排出的数与双端队

    2024年02月07日
    浏览(58)
  • 学习c#的第二十四天

    目录 C# 事件(Event) 事件概述 如何订阅和取消订阅事件 以编程方式订阅事件 使用匿名函数订阅事件 取消订阅 如何发布符合 .NET 准则的事件 发布基于 EventHandler 模式的事件 如何在派生类中引发基类事件 如何实现接口事件 如何实现自定义事件访问器 示例 事件(Event) 基本

    2024年02月04日
    浏览(41)
  • MFC补充第十四天 句柄嫁接与子类化

    句柄嫁接与子类化: a)Attach和Detach就是单纯的嫁接与分离函数。 对象一旦嫁接入一个句柄,就可以自由地调用CWnd或其派生类的功能。 b)子类化Subclass内部包含Attach,额外再增加一个消息转拨到派生类(SubClass就是子类) c)SubClassWindow函数内部核心功能就是Attach和::SetWindowLong

    2024年02月16日
    浏览(48)
  • 蓝桥杯十四天冲刺班 第十四天《考场经验 | 历年考点 | 蓝桥杯押题》《C,JAVA,PY在蓝桥杯中必须要会用的容器 | 集合》(3K+字解析)

     📒博客首页:Sonesang的博客 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 ❤️ :热爱Java与算法学习,期待一起交流! 🙏作者水平很有限,如果发现错误,求告知,多谢! 🌺有问题可私信交流!!!   目录 算法 实力 = 知识点+刷题量+速度+灵活的大脑 C++组知识点 java组知识点

    2023年04月15日
    浏览(42)
  • Python学习笔记第六十四天(Matplotlib 网格线)

    我们可以使用 pyplot 中的 grid() 方法来设置图表中的网格线。 grid() 方法语法格式如下: 参数说明: b:可选,默认为 None,可以设置布尔值,true 为显示网格线,false 为不显示,如果设置 **kwargs 参数,则值为 true。 which:可选,可选值有 ‘major’、‘minor’ 和 ‘both’,默认为

    2024年02月12日
    浏览(44)
  • 第五十四天学习记录:C语言进阶:动态内存管理Ⅱ

    1、对NULL指针的解引用操作 2、对动态开辟的内存的越界访问 3、对非动态开辟内存的free 4、使用free释放动态开辟内存的一部分 5、对同一块动态内存多次释放 6、动态开辟内存忘记释放(内存泄漏) 问:realloc的第一个参数的指针地址必须是malloc或calloc创建的在堆上的地址吗?

    2024年02月06日
    浏览(39)
  • 15天学习MySQL计划(运维篇)分库分表-监控-第十四天

    1.介绍 1.问题分析 ​ 随着互联网及移动互联网的发展,应用系统的数据量也是成指数式增加,若采用但数据进行数据存储,存在以下性能瓶颈: IO瓶颈:热点数据太多,数据库缓存不足,产生大量磁盘IO,效率较低。请求数据太多,带宽不够,网络IO瓶颈。 CPU瓶颈:排序,分

    2024年02月05日
    浏览(49)
  • 从零开始的力扣刷题记录-第六十四天

    题目描述: 给你一个下标从 0 开始的整数数组 nums 。在一步操作中,你可以执行以下步骤: 从 nums 选出 两个 相等的 整数 从 nums 中移除这两个整数,形成一个 数对 请你在 nums 上多次执行此操作直到无法继续执行。 返回一个下标从 0 开始、长度为 2 的整数数组 answer 作为答

    2024年02月11日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包