剖析【C++】——类和对象(下篇)——超详解——小白篇

news2024/11/25 20:19:49

目录

1.再谈构造函数

1.1 构造函数体赋值

1.2 初始化列表

1.3 explicit 关键字

2. Static成员

2.1 概念

2.2 特性

3. 友元

3.1 友元函数

3.2 友元类

3.3总结:

4. 内部类

1.概念

2.特性

示例代码:

代码分析

3.总结

5.再次理解类和对象

1. 抽象现实生活中的实体

2. 用类描述实体

3. 实例化对象

4. 总结类和对象的关系


1.再谈构造函数

1.1 构造函数体赋值

构造函数是用来初始化对象的特殊函数。当我们创建一个对象时,编译器会自动调用构造函数来给对象的成员变量赋值。

简单理解:当你买一个新手机,打开包装,这就像调用了构造函数。手机里的默认应用程序就像成员变量的初始值。这些默认应用虽然是预装的,但我们可以再安装新的应用,这相当于构造函数体内的赋值操作。

示例代码:

class MyClass {
public:
    int x;
    int y;
    MyClass(int a, int b) : x(a), y(b) {}  // 初始化列表
};

在上面的代码中,当我们创建一个 MyClass 对象时,比如 MyClass obj(10);,构造函数 MyClass(int value) 会被调用,x 会被赋值为 10。但是这个过程叫赋值,不叫初始化,因为我们可以在构造函数内多次赋值。

总结:构造函数体内的赋值可以多次进行,而初始化只能进行一次。

1.2 初始化列表

初始化列表 是另一种在创建对象时给成员变量赋初值的方法。这种方法更高效,因为它在对象创建时就直接赋值,而不是先创建对象然后再赋值。

简单理解:假设你买了一辆新车,初始化列表就像你在购车前已经决定好车的颜色、型号等,而不是买车后再去喷漆改装。

示例代码:

class MyClass {
public:
    int x;
    int y;
    MyClass(int a, int b) : x(a), y(b) {}  // 初始化列表
};

在上面的代码中,: x(a), y(b) 就是初始化列表,它在构造函数体执行之前完成成员变量的初始化。

注意事项

  1. 每个成员变量在初始化列表中只能出现一次。
  2. 以下成员必须在初始化列表中初始化:
    • 引用类型成员变量
    • const 成员变量
    • 自定义类型成员变量(如果该类没有默认构造函数)

示例代码:

class MyClass {
public:
    const int x;
    int &y;
    MyClass(int a, int &b) : x(a), y(b) {}  // 必须在初始化列表中初始化
};

在这段代码中,const int xint &y 必须在初始化列表中进行初始化,因为 const 成员变量和引用类型成员变量在对象创建时就需要确定其初始值。

总结:尽量使用初始化列表,因为它对自定义类型成员变量更高效。

1.3 explicit 关键字

构造函数不仅可以用来创建对象,还可以用来进行类型转换。对于只有一个参数的构造函数,如果不使用 explicit 关键字,编译器会自动进行隐式类型转换。

简单理解:假设有一个银行系统,你有一个账户类。如果允许隐式转换,就像允许陌生人随意把你的银行账号转换成他们的账户,这很危险。所以我们需要 explicit 关键字来禁止这种转换。

示例代码:

class MyClass {
public:
    int x;
    explicit MyClass(int value) : x(value) {}  // 使用 explicit 关键字
};

void func(MyClass obj) {
    // do something
}

int main() {
    MyClass obj1(10);  // 正常调用构造函数
    // MyClass obj2 = 10;  // 错误,explicit 禁止隐式转换
    // func(20);  // 错误,explicit 禁止隐式转换
    return 0;
}

在上面的代码中,explicit 关键字禁止了构造函数的隐式转换,这样可以避免潜在的错误和提高代码的可读性。

总结:使用 explicit 关键字可以防止构造函数被用于隐式转换,确保代码的安全性和可读性。

2. Static成员

2.1 概念

在C++中,声明为static的类成员称为类的静态成员。静态成员分为静态成员变量和静态成员函数。

  • 静态成员变量:用static修饰的成员变量。
  • 静态成员函数:用static修饰的成员函数。

简单理解:静态成员就像学校里的公共设施,比如学校的大门(静态成员变量)和学校的公告栏(静态成员函数),它们是所有学生(类的对象)共享的,而不是某个学生独有的。

初始化:静态成员变量必须在类外进行初始化。

面试题:实现一个类,计算程序中创建了多少个类对象。

示例代码

#include <iostream>
using namespace std;

class MyClass {
public:
    static int count;  // 静态成员变量声明

    MyClass() {
        count++;  // 每创建一个对象,count加1
    }

    // 静态成员函数
    static int getCount() {
        return count;
    }
};

// 静态成员变量在类外定义并初始化
int MyClass::count = 0;

int main() {
    MyClass obj1;
    MyClass obj2;
    MyClass obj3;
    cout << "Created objects: " << MyClass::getCount() << endl;
    return 0;
}

在这段代码中,MyClass类的静态成员变量count记录了创建的对象数量,每创建一个对象,count就加1。静态成员函数getCount返回当前的对象数量。

2.2 特性

  1. 静态成员为所有类对象共享:静态成员变量存放在静态区,不属于某个具体的对象。
  2. 静态成员变量必须在类外定义:定义时不添加static关键字,类中只是声明。
  3. 访问静态成员:静态成员可以通过类名::静态成员对象.静态成员访问。
  4. 静态成员函数没有this指针:不能访问任何非静态成员。
  5. 静态成员受访问限定符限制:静态成员同样受publicprotectedprivate访问限定符的限制。

示例代码

class MyClass {
public:
    static int staticVar;  // 静态成员变量声明
    int nonStaticVar;      // 非静态成员变量

    static void staticFunc() {
        // 静态成员函数不能访问非静态成员
        // cout << nonStaticVar;  // 错误
        cout << staticVar;  // 正确
    }

    void nonStaticFunc() {
        cout << staticVar;  // 非静态成员函数可以访问静态成员
        cout << nonStaticVar;  // 非静态成员函数可以访问非静态成员
    }
};

// 静态成员变量定义
int MyClass::staticVar = 0;

问题解答

1.静态成员函数可以调用非静态成员函数吗? 不可以。静态成员函数没有this指针,所以不能访问类的非静态成员(包括非静态成员函数)。

示例代码

class MyClass {
public:
    static void staticFunc() {
        // nonStaticFunc();  // 错误,静态成员函数不能调用非静态成员函数
    }

    void nonStaticFunc() {
        cout << "Non-static member function called." << endl;
    }
};

2.非静态成员函数可以调用类的静态成员函数吗? 可以。非静态成员函数可以访问类的所有成员,包括静态成员。

示例代码

class MyClass {
public:
    static void staticFunc() {
        cout << "Static member function called." << endl;
    }

    void nonStaticFunc() {
        staticFunc();  // 非静态成员函数可以调用静态成员函数
    }
};

3. 友元

友元提供了一种特殊的机制,可以让一个类允许另一个类或函数访问其私有或受保护的成员。虽然友元增加了便利性,但也会破坏封装性,增加代码耦合度,因此使用时需谨慎。

3.1 友元函数

问题描述:在重载 operator<< 时,无法将其重载为成员函数,因为 cout 作为输出流对象和 this 指针会竞争第一个参数的位置。为了使 cout 成为第一个参数,我们需要将 operator<< 重载为全局函数。但全局函数不能直接访问类的私有成员,这时就需要友元函数来解决这个问题。

友元函数的特点

  1. 可以访问类的私有和保护成员,但不属于类的成员函数。
  2. 不能用 const 修饰。
  3. 可以在类定义的任何地方声明,不受类访问限定符限制。
  4. 一个函数可以是多个类的友元函数。
  5. 友元函数的调用与普通函数的调用相同。

示例代码

#include <iostream>
using namespace std;

class MyClass {
private:
    int value;

public:
    MyClass(int v) : value(v) {}

    // 声明友元函数
    friend ostream& operator<<(ostream& os, const MyClass& obj);
};

// 定义友元函数
ostream& operator<<(ostream& os, const MyClass& obj) {
    os << obj.value;
    return os;
}

int main() {
    MyClass obj(42);
    cout << obj << endl;  // 输出: 42
    return 0;
}

在上面的代码中,operator<< 被定义为友元函数,因此它可以访问 MyClass 类的私有成员 value

3.2 友元类

友元类是一种类的所有成员函数都可以访问另一个类的私有和保护成员的机制。

特点

  1. 友元关系是单向的,不具有交换性。例如,如果 A 类是 B 类的友元,那么 B 类可以访问 A 类的私有成员,但反过来 A 类不能访问 B 类的私有成员。
  2. 友元关系不能传递。如果 BA 的友元,而 CB 的友元,这并不意味着 CA 的友元。
  3. 友元关系不能继承。

示例代码

#include <iostream>
using namespace std;

class Date;

class Time {
private:
    int hour;
    int minute;

public:
    Time(int h, int m) : hour(h), minute(m) {}

    // 声明Date类为友元类
    friend class Date;
};

class Date {
private:
    int day;
    int month;
    int year;

public:
    Date(int d, int m, int y) : day(d), month(m), year(y) {}

    void displayTime(const Time& t) {
        // 访问Time类的私有成员
        cout << "Time: " << t.hour << ":" << t.minute << endl;
    }
};

int main() {
    Time t(10, 30);
    Date d(1, 5, 2023);
    d.displayTime(t);  // 输出: Time: 10:30
    return 0;
}

在上面的代码中,Date 类被声明为 Time 类的友元类,因此 Date 类的成员函数可以访问 Time 类的私有成员。

3.3总结

  • 友元函数和友元类允许访问私有和保护成员,但要谨慎使用,因为这会增加代码的耦合性。
  • 友元关系是单向的,不可传递。
  • 使用友元可以解决一些特殊情况下的访问权限问题,如重载运算符等。

4. 内部类

1.概念

内部类 是指定义在另一个类内部的类。它是一个独立的类,不属于外部类,不能通过外部类的对象访问其成员。

简单理解:就像一家大公司的部门(内部类)和公司(外部类),部门是独立的,但仍然是公司的一部分,外部类对内部类没有特别的访问权限。

注意事项

  • 内部类就像是外部类的友元类,内部类可以通过外部类的对象参数访问外部类的所有成员。
  • 外部类对内部类没有友元访问权限,不能访问内部类的私有成员。

2.特性

  1. 内部类的位置:内部类可以定义在外部类的 publicprotectedprivate 区域。
  2. 访问外部类的静态成员:内部类可以直接访问外部类的静态成员,而不需要外部类的对象或类名。
  3. 大小计算sizeof(外部类) 只计算外部类的大小,与内部类无关。

示例代码:

#include <iostream>
using namespace std;

class OuterClass {
private:
    int outerVar;
    static int outerStaticVar;

public:
    OuterClass(int val) : outerVar(val) {}

    // 定义内部类
    class InnerClass {
    public:
        void displayOuter(OuterClass& outer) {
            // 访问外部类的非静态成员需要外部类的对象
            cout << "Outer class non-static member: " << outer.outerVar << endl;
        }

        void displayOuterStatic() {
            // 直接访问外部类的静态成员
            cout << "Outer class static member: " << outerStaticVar << endl;
        }
    };

    // 外部类中的成员函数可以创建内部类的对象
    void createInner() {
        InnerClass inner;
        inner.displayOuter(*this);
        inner.displayOuterStatic();
    }
};

// 定义并初始化外部类的静态成员
int OuterClass::outerStaticVar = 10;

int main() {
    OuterClass outer(5);
    outer.createInner();

    // 外部类对象不能直接访问内部类成员
    // outer.InnerClass inner;  // 错误

    // 创建内部类对象
    OuterClass::InnerClass inner;
    inner.displayOuter(outer);
    inner.displayOuterStatic();

    return 0;
}

代码分析

  1. 内部类的位置InnerClass 定义在 OuterClasspublic 区域内,可以访问外部类的静态和非静态成员。
  2. 访问外部类的静态成员InnerClass 中的 displayOuterStatic 函数直接访问 OuterClass 的静态成员 outerStaticVar
  3. 访问外部类的非静态成员displayOuter 函数通过 OuterClass 的对象参数 outer 访问其非静态成员 outerVar

3.总结

  • 内部类 是独立的类,可以定义在外部类的任何访问区域。
  • 内部类可以直接访问外部类的静态成员,不需要外部类的对象或类名。
  • 内部类可以通过外部类的对象参数访问外部类的非静态成员。
  • 外部类不能访问内部类的私有成员,内部类也不会影响外部类的大小计算。

5.再次理解类和对象

1. 抽象现实生活中的实体

在现实生活中,计算机无法直接认识物理世界中的实体,如洗衣机。为了让计算机理解这些实体,我们需要通过面向对象的语言(如C++)对它们进行抽象和描述。

简单理解:假设你想让计算机认识洗衣机。首先,你需要在头脑中抽象出洗衣机的属性和功能。比如,洗衣机有颜色、品牌、容量等属性,还有启动、停止、洗涤等功能。

2. 用类描述实体

一旦你在人为思想层面对洗衣机有了清晰的认识,就需要用某种编程语言(如C++)将这种认识转化为计算机能理解的格式。我们使用“类”来描述洗衣机。

示例代码

class WashingMachine {
public:
    // 属性
    string color;
    string brand;
    int capacity;

    // 方法
    void start() {
        cout << "Washing machine started." << endl;
    }

    void stop() {
        cout << "Washing machine stopped." << endl;
    }

    void wash() {
        cout << "Washing machine is washing clothes." << endl;
    }
};

在上面的代码中,我们定义了一个类 WashingMachine,它包含了洗衣机的属性(颜色、品牌、容量)和方法(启动、停止、洗涤)。

3. 实例化对象

定义了类之后,计算机还不能理解洗衣机是什么。我们需要通过类来实例化具体的洗衣机对象。

示例代码

int main() {
    // 实例化一个洗衣机对象
    WashingMachine myWasher;

    // 给属性赋值
    myWasher.color = "White";
    myWasher.brand = "LG";
    myWasher.capacity = 7;

    // 调用方法
    myWasher.start();
    myWasher.wash();
    myWasher.stop();

    return 0;
}

在这段代码中,我们创建了一个 WashingMachine 对象 myWasher,并为其属性赋值,然后调用其方法来模拟洗衣机的行为。

4. 总结类和对象的关系

是对某一类实体的抽象和描述。类定义了这些实体具有的属性和方法,形成了一种新的自定义类型。

对象 是类的实例,是具体的实体。通过实例化类,我们创建对象,然后可以使用这些对象来模拟现实中的实体。

现实生活中的模拟

  • 抽象:你在人为思想层面对洗衣机进行认识,确定它的属性和功能。
  • :用C++类来描述洗衣机的属性和功能,将这种描述输入计算机中。
  • 实例化:通过类实例化具体的洗衣机对象,计算机才真正理解和模拟洗衣机的行为。

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

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

相关文章

vue2转vue3初步下载pnpm遇到的问题 pnpm : 无法加载文件 D:\nodejs\pnpm.ps1

安装pnpm npm install -g pnpm pnpm -v 提示&#xff1a; 解决&#xff1a;nvm install 18.18.0 下载最稳定版本的nodejs nvm use 18.18.0 然后注意重新下载删除pnpm npm uninsta17 -g pnpm npm install -g pnpmlatest 在vscode使用pnpm报错 解决&#xff1a;管理员运行Windo…

C语言.数据结构.单链表

数据结构.单链表 1.链表的概念及结构2.单链表的实现2.1链表的打印2.2节点的申请2.3单链表的尾插2.4单链表的头插2.5单链表的尾删2.6单链表的头删2.7单链表节点的查找2.8在指定位置之前插入数据2.9在指定位置之后插入数据2.10删除pos节点2.11删除pos之后的节点2.12单链表的销毁2…

新一代最强开源UI自动化测试神器Playwright(Java版)六(断言)

Playwright是一个流行的UI自动化测试框架&#xff0c;用于编写UI自动化测试。在测试中&#xff0c;断言是一个非常重要的概念&#xff0c;用于验证测试的结果是否符合预期。Playwright提供了一些内置的断言函数&#xff0c;可以帮助测试人员编写更加简洁和可读的测试代码。本文…

为啥装了erlang,还报错erl: command not found?

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 问题背景&#xff1a; 在一台不通外网的服务器上装rabbitmq&#xff0c;然后在启动的时候&#xff0c;遇到了报错 “/usr/lib/…

走进中国照明百强西顿照明,解码数字化战略与经营增长

5月24日&#xff0c;纷享销客携领20多位企业高管走进纷享销客【数字化标杆】游学示范基地——西顿照明&#xff0c;并参访其位于惠州总部的光之家及灯巢&#xff0c;特邀广东端到端管理咨询兼纷享管理服务专家陈立云、西顿照明CIO唐勇作主题分享&#xff0c;为嘉宾带来一场解码…

【成品设计】基于STM32单片机的饮水售卖机

基于STM32单片机的饮水售卖机 所需器件&#xff1a; STM32最小系统板。RFID&#xff1a;MFRC-522用于IC卡检测。OLED屏幕&#xff1a;用于显示当前水容量、系统状态等。水泵软管&#xff1a;用于抽水。水位传感器&#xff08;3个&#xff09;&#xff1a;用于分别标定&#x…

Qt-qrencode生成二维码

Qt-qrencode开发-生成二维码&#x1f4c0; 文章目录 Qt-qrencode开发-生成二维码&#x1f4c0;[toc]1、概述&#x1f4f8;2、实现效果&#x1f4bd;3、编译qrencode&#x1f50d;4、在QT中引入编译为静态库的QRencode5、在Qt中直接使用QRencode源码6、在Qt中使用QRencode生成二…

数据可视化:解析其在现代生活中的日益重要地位

数据可视化为什么对我们的生活影响越来越大&#xff1f;这是一个值得探讨的话题。在信息化时代&#xff0c;数据无处不在&#xff0c;海量的数据不仅改变了商业模式&#xff0c;也深刻影响了我们的日常生活。数据可视化作为一种将复杂数据转化为直观图表、图形的技术&#xff0…

ubuntu22.04.3 vmware虚拟机配置共享文件夹 解决无法挂载/mnt/hgfs,血泪教训

一、背景介绍 在VMware Workstation 17 Pro上创建ubuntu22.04.3虚拟机&#xff0c;实现在ubuntu系统中共享windows的文件夹。按照网上方法试了大半&#xff0c;都没法解决&#xff0c;最终发现是vmware tools安装出现了问题&#xff0c;成功安装后&#xff0c;解决。 二、配置…

【软件测试】软件测试概念 | 测试用例 | BUG | 开发模型 | 测试模型 | 生命周期

文章目录 一、什么是软件测试1.什么是软件测试2.软件测试和调试的区别测试人员需要的素养 二、软件测试概念1.需求1.需求的定义2.测试人员眼中的需求 2.测试用例1.测试用例概念 3.BUG 软件错误4、开发模型和测试模型1.软件的生命周期2.开发模型1.瀑布模型2.螺旋模型3.增量、迭代…

端午档新片已预热,强业绩修复的影视股为何仍在徘徊?

随着端午临近&#xff0c;假期13部新片开始定档。据猫眼专业版显示&#xff0c;截至5月29日11时&#xff0c;即将上映的这13部新片&#xff0c;预售总票房已达到2155万。 受此消息影响&#xff0c;近日影视股出现了小幅的触底震荡反弹迹象&#xff0c;其中IMAX中国(01970)反弹…

记录Win11安装打印机驱动过程

1. 首先下载打印机对应型号的驱动 可以从这里下载&#xff1a;打印机驱动,打印机驱动下载 - 打印机驱动网 2. 下载 3. 打开控制面板-->设备和打印机 找到目标打印机添加设备即可 新增打印纸张尺寸

上传图片并显示#Vue3#后端接口数据

上传图片并显示#Vue3#后端接口数据 效果&#xff1a; 代码&#xff1a; <!-- 上传图片并显示 --> <template><!-- 上传图片start --><div><el-form><el-form-item><el-uploadmultipleclass"avatar-uploader"action"…

借助AI大模型,三分钟原创一部儿童故事短视频(附完整操作步骤)

前面文章的介绍&#xff0c;我们可以通过在自己笔记本电脑上部署的Llama 3大模型生成文章、文本润色、生成摘要等。今天我们更进一步&#xff0c;在文本的基础上&#xff0c;快速制作一部儿童故事短视频&#xff0c;且可根据自己需要完全原创…… 前提&#xff1a;有AI大模型对…

禅道迁移,linux一键安装版

问题描述&#xff1a;公司需要迁移禅道到另外一台服务器&#xff0c;没迁移过&#xff0c;去官网看了之后成功迁移&#xff0c;其中遇到了很多坑,希望对你们有所帮助。 禅道版本 迁移的版本一致&#xff0c;我的版本是18.3&#xff0c;18.3下载页面 其他版本下载 先进入检…

【SAP HANA 33】前端参数多选情况下HANA如何使用IN来匹配?

场面描述: 在操作界面经常会出现某个文本框需要多选的情况,然后后台需要根据多选的值进行匹配搜索。 一般处理的情况是: 1、在Java后端动态生成SQL 2、不改变动态SQL的情况,直接当做一个正常的参数进行传递 本次方案是第二个,直接当做一个正常的字符串参数进行传递即…

USB主机模式——Android

理论 摘自&#xff1a;USB 主机和配件概览 | Connectivity | Android Developers (google.cn) Android 通过 USB 配件和 USB 主机两种模式支持各种 USB 外围设备和 Android USB 配件&#xff08;实现 Android 配件协议的硬件&#xff09;。 在 USB 主机模式下&#xff0…

迪普科技:量子安全“先行者”退场?

今年4月&#xff0c;迪普科技&#xff08;300768 SHE&#xff09;接受机构调研时被问及“公司对量子通讯所必需的安全产品在继续研究吗”时表示&#xff1a;“公司曾为量子通讯网络提供安全产品解决方案&#xff0c;从这点说与其他领域的安全产品区别不大”。换句话说&#xff…

mybatis异常:Invalid bound statement (not found): com.lm.mapper.ArticleMapper.list

现象&#xff1a; 原因&#xff1a; 无效绑定&#xff0c;应该是mybatis最常见的一个异常了&#xff0c;接口与XML文件没绑定。首先&#xff0c;mapper接口并没有实现类&#xff0c;所以框架会通过JDK动态代理代理模式获取接口的代理实现类&#xff0c;进而根据接口全限定类名…

SNCScan:针对SAP安全网络通信(SNC)的安全分析与评估工具

关于SNCScan SNCScan是一款针对SAP安全网络通信&#xff08;SNC&#xff09;的安全分析与评估工具&#xff0c;该工具旨在帮助广大研究人员分析SAP安全网络通信&#xff08;SNC&#xff09;&#xff0c;并分析和检测SNC配置与SAP组件中的潜在问题。 SNC系统参数 SNC基础 SAP协…