C++类与对象(上):从入门到实践

news2025/4/3 6:29:50

目录

一、引言

二、面向过程和面向对象初步认识

2.1 面向过程编程

2.2 面向对象编程

三、类的引入

四、类的定义

4.1 定义格式

4.2 定义方式

4.3 成员变量命名规则建议

五、类的访问限定符及封装

5.1 访问限定符

5.2 封装

六、类的作用域

七、类的实例化

7.1 概念阐述

7.2 实例化过程及内存占用

7.3 形象比喻

八、类的对象大小的计算

8.1 计算规则

8.2 特殊情况 - 空类

九、类成员函数的 this 指针

9.1 this 指针的引出

9.2 this 指针的特性

9.3 面试题相关分析

十、C 语言和 C++ 实现栈的对比

10.1 C 语言实现栈

10.2 C++ 实现栈

十一、总结


一、引言

在编程领域中,C++ 作为一门强大且广泛应用的编程语言,其面向对象编程(OOP)特性中的类与对象概念是核心基础。理解并掌握类与对象相关知识,对于深入学习 C++ 以及开发高质量的软件系统至关重要。本文将深入探讨 C++ 类与对象(上)的关键内容,涵盖从基本概念到实际代码示例的全方位解析,助力读者夯实基础。

二、面向过程和面向对象初步认识

2.1 面向过程编程

面向过程编程(POP)是一种以操作步骤为核心的编程范式。它将程序视为一系列顺序执行的操作,数据和操作相互分离。例如,在使用 C 语言开发一个简单的学生成绩管理系统时,会分别定义函数来实现成绩录入、平均分计算、成绩排序等功能,而学生成绩数据则作为参数在这些函数间传递。这种编程方式强调的是过程和步骤,按照事先设计好的流程依次处理数据。

2.2 面向对象编程

面向对象编程(OOP)则将数据和对数据的操作封装在一起,形成对象。对象具有属性(数据)和行为(方法),通过对象之间的交互来完成任务。在 C++ 中,通过类来定义对象的类型,类是对具有相同属性和行为的对象的抽象描述。以  Student  类为例,它可以包含学生的姓名、年龄、成绩等属性,以及计算成绩等级、打印学生信息等方法。这种编程方式更符合人们对现实世界的认知,将事物抽象为对象,通过对象的协作来解决问题。

三、类的引入

在 C 语言中,结构体( struct )主要用于定义数据结构,只能包含变量。而在 C++ 中,结构体的功能得到了扩展,不仅可以定义变量,还能定义函数。以下是一个用 C++ 实现栈(Stack)的示例代码,用以展示类的引入所带来的优势:


cpp

// 定义数据类型别名

typedef int DataType;

// 定义栈结构体

struct Stack {

    // 初始化栈

    void Init(size_t capacity) {

        _array = (DataType*)malloc(sizeof(DataType) * capacity);

        if (nullptr == _array) {

            perror("malloc申请空间失败");

            return;

        }

        _capacity = capacity;

        _size = 0;

    }



    // 入栈操作

    void Push(const DataType& data) {

        // 此处省略扩容逻辑

        _array[_size] = data;

        ++_size;

    }



    // 获取栈顶元素

    DataType Top() {

        return _array[_size - 1];

    }



    // 销毁栈

    void Destroy() {

        if (_array) {

            free(_array);

            _array = nullptr;

            _capacity = 0;

            _size = 0;

        }

    }



    // 栈数据数组

    DataType* _array;

    // 栈容量

    size_t _capacity;

    // 栈中元素个数

    size_t _size;

};



int main() {

    Stack s;

    s.Init(10);

    s.Push(1);

    s.Push(2);

    s.Push(3);

    std::cout << s.Top() << std::endl;

    s.Destroy();

    return 0;

}

在 C++ 中,虽然  struct  可以实现上述功能,但更推荐使用  class  关键字来定义类,因为  class  能更好地体现面向对象编程的特性,如访问控制、封装等。

四、类的定义

4.1 定义格式

类的定义使用  class  关键字,基本格式如下:

cpp

class ClassName {

public:

    // 公有成员函数和变量,在类外可以直接访问

    void memberFunction1();

    int publicVariable;

protected:

    // 保护成员函数和变量,类及其派生类可以访问

    void memberFunction2();

    int protectedVariable;

private:

    // 私有成员函数和变量,仅在类内部可以访问

    void memberFunction3();

    int privateVariable;

};

需要注意的是,类定义结束时后面的分号不能省略。在类体中,包含的内容称为类的成员:其中的变量称为类的属性或成员变量;其中的函数称为类的方法或成员函数。

4.2 定义方式

类有两种常见的定义方式:

1. 声明和定义全部放在类体中:将成员函数的声明和定义都写在类体内部。例如:


cpp

class Date {

public:

    // 初始化日期函数

    void Init(int year, int month, int day) {

        _year = year;

        _month = month;

        _day = day;

    }

    // 打印日期函数

    void Print() {

        std::cout << _year << "-" << _month << "-" << _day << std::endl;

    }

private:

    int _year;

    int _month;

    int _day;

};



当成员函数在类中定义时,编译器可能会将其当成内联函数处理。内联函数的优势在于,在编译阶段会将函数调用处替换为函数体代码,减少函数调用的开销,提高程序执行效率,但可能会增加目标代码的体积。

1. 类声明放在.h文件中,成员函数定义放在.cpp文件中:将类的声明放在头文件(.h)中,而成员函数的定义放在实现文件(.cpp)中。以  Person  类为例:

person.h



cpp

class Person {

public:

    // 显示基本信息函数声明

    void showInfo();

    char* _name; // 姓名

    char* _sex; // 性别

    int _age; // 年龄

};





person.cpp



cpp

#include "person.h"

#include <iostream>

// 显示基本信息函数定义

void Person::showInfo() {

    std::cout << _name << "-" << _sex << "-" << _age << std::endl;

}



在这种方式下,在类体外定义成员函数时,需要使用  ::  作用域操作符指明成员函数属于哪个类域。一般情况下,更推荐采用第二种方式,它能更好地实现代码的分离和组织,提高代码的可读性和可维护性。在实际开发中,为了方便演示可能会使用第一种方式,但在正式工作中应尽量使用第二种方式。

4.3 成员变量命名规则建议

在定义成员变量时,为了避免混淆,建议使用前缀或后缀来标识。例如,对于  Date  类中的成员变量  year ,如果不做区分,在成员函数中很难分清是成员变量还是函数形参:


 

cpp

class Date {

public:

    void Init(int year) {

        // 这里的year到底是成员变量,还是函数形参?

        year = year;

    }

private:

    int year;

};





为了清晰区分,一般建议这样命名:



cpp

class Date {

public:

    void Init(int year) {

        _year = year;

    }

private:

    int _year;

};



或者


cpp

class Date {

public:

    void Init(int year) {

        mYear = year;

    }

private:

    int mYear;

};



具体的命名方式可以根据公司或项目的要求来确定,关键是要做到清晰、易区分。

五、类的访问限定符及封装


5.1 访问限定符

C++ 提供了三种访问限定符,用于控制类成员的访问权限:

- public(公有):用  public  修饰的成员在类外可以直接被访问。例如:


cpp

class MyClass {

public:

    int publicVariable;

    void publicFunction() {

        std::cout << "This is a public function." << std::endl;

    }

};



int main() {

    MyClass obj;

    obj.publicVariable = 10;

    obj.publicFunction();

    return 0;

}



- protected(保护): protected  修饰的成员在类外不能直接被访问,但在类的派生类(涉及继承概念,后续会深入探讨)中可以被访问。它主要用于在类的继承体系中,让派生类能够访问基类的某些成员。

- private(私有): private  修饰的成员仅在类内部可以被访问,类外无法直接访问。例如:


cpp

class MyClass {

private:

    int privateVariable;

    void privateFunction() {

        std::cout << "This is a private function." << std::endl;

    }

public:

    void accessPrivate() {

        privateVariable = 5;

        privateFunction();

    }

};



int main() {

    MyClass obj;

    // 以下操作会报错,无法在类外访问私有成员

    // obj.privateVariable = 10;

    // obj.privateFunction();

    obj.accessPrivate();

    return 0;

}



访问限定符的作用域从其出现的位置开始,直到下一个访问限定符出现或类结束。例如:


cpp

class A {

public:

    void func1();

private:

    int data;

    void func2();

protected:

    void func3();

};



5.2 封装

封装是面向对象编程的重要特性之一,它将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。以电脑为例,对于普通用户而言,不需要了解 CPU、主板等内部硬件的工作原理和具体实现细节,只需要通过开关、键盘、鼠标等外部接口就能使用电脑完成各种任务。

在 C++ 中,通过类和访问限定符来实现封装。例如,定义一个  Date  类:


cpp

class Date {

public:

    // 初始化日期函数

    void Init(int year, int month, int day) {

        _year = year;

        _month = month;

        _day = day;

    }

    // 打印日期函数

    void Print() {

        std::cout << _year << "-" << _month << "-" << _day << std::endl;

    }

private:

    int _year;

    int _month;

    int _day;

};



在这个  Date  类中,日期的具体存储变量  _year 、 _month 、 _day  被设为私有,外部代码无法直接访问和修改这些变量。外部只能通过公有成员函数  Init  和  Print  来对  Date  对象进行初始化和查看操作,从而实现了数据的隐藏和保护,提高了代码的安全性和可维护性。

六、类的作用域

类定义了一个新的作用域,类的所有成员都在这个作用域内。在类外访问类的公有成员时,需要通过类对象或指针来进行。例如:

cpp

class MyClass {

public:

    int value;

    void printValue() {

        std::cout << value << std::endl;

    }

};



int main() {

    MyClass obj;

    obj.value = 10;

    obj.printValue();

    return 0;

}



这里  value  和  printValue  函数都在  MyClass  的作用域内。在  main  函数中,首先创建了  MyClass  的对象  obj ,然后通过对象名  obj  来访问其公有成员变量  value  并赋值,以及调用公有成员函数  printValue  来输出变量的值。

当在类体外定义成员函数时,需要使用  ::  作用域操作符来指明该成员函数属于哪个类域。例如:


cpp

class Person {

public:

    void PrintPersonInfo();

private:

    char _name[20];

    char _gender[3];

    int _age;

};



// 定义Person类的PrintPersonInfo函数

void Person::PrintPersonInfo() {

    std::cout << _name << " " << _gender << " " << _age << std::endl;

}



在上述代码中, Person::PrintPersonInfo  明确表示  PrintPersonInfo  函数属于  Person  类域。

七、类的实例化

7.1 概念阐述

用类类型创建对象的过程,称为类的实例化。类是对对象的抽象描述,类似于一个模型或蓝图,它限定了类有哪些成员,但定义类本身并没有分配实际的内存空间来存储具体的数据。例如,入学时填写的学生信息表可以看作是一个类,它描述了学生信息的结构和属性,但这个表格本身并不包含具体某个学生的实际信息。类就像谜语,对谜底进行描述,而谜底就是谜语的一个实例。比如谜语“年纪不大,胡子一把,主人来了,就喊妈妈”,谜底“山羊”就是一个实例。

7.2 实例化过程及内存占用

一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,用于存储类的成员变量。以下通过代码示例来说明:


 

cpp

class Person {

public:

    int _age;

};



int main() {

    // 尝试直接给类的成员变量赋值,编译失败

    // Person._age = 100; // 编译失败:error C2059: 语法错误:“.”

    Person p1;

    p1._age = 20;

    Person p2;

    p2._age = 25;

    return 0;

}



在上述代码中, Person  类只是一个模板, Person p1;  和  Person p2;  才是将  Person  类实例化,创建了两个  Person  类型的对象  p1  和  p2 ,每个对象都有自己独立的内存空间来存储成员变量  _age 。

7.3 形象比喻

类实例化出对象就像现实中使用建筑设计图建造房子,类就像是设计图,只设计出需要什么东西,但并没有实体的建筑存在;而实例化出的对象则是根据设计图建造好的实际房子,能够实际存储数据,占用物理空间。

八、类的对象大小的计算

8.1 计算规则

一个类的大小实际就是该类中“成员变量”之和,同时要考虑内存对齐规则:

1. 第一个成员的存储位置:第一个成员在与结构体偏移量为 0 的地址处。

2. 成员变量的对齐规则:其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。对齐数是编译器默认的一个对齐数与该成员大小的较小值(例如在 VS 编译器中默认对齐数为 8 )。

3. 类的总大小:类的总大小为最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。

4. 嵌套结构体情况:如果类中嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍处,类的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

例如:


cpp

class A {

public:

    void PrintA() {

        std::cout << _a << std::endl;

    }

private:

    char _a;

};



假设编译器默认对齐数为 8, char  类型大小为 1, A  类中只有一个  char  类型的成员变量  _a 。按照内存对齐规则, A  类对象大小为 1(因为  char  类型变量存储在偏移量为 0 的地址处,满足对齐要求)。

8.2 特殊情况 - 空类

空类比较特殊,编译器会给空类一个字节来唯一标识这个类的对象。例如:


cpp

class EmptyClass {};



此时  sizeof(EmptyClass)  的结果为 1。这是因为虽然空类没有成员变量,但为了能够区分不同的空类对象,编译器会为其分配一个字节的空间。

九、类成员函数的 this 指针

9.1 this 指针的引出

以  Date  类为例:


cpp

class Date {

public:

    void Init(int year, int month, int day) {

        _year = year;

        _month = month;

        _day = day;

    }

    void Print() {

        std::cout << _year << "-" << _month << "-" << _day << std::endl;

    }

private:

    int _year;

    int _month;

    int _day;

};



int main() {

    Date d1, d2;

    d1.Init(2022, 1, 11);

    d2.Init(2022, 1, 12);

    d1.Print();

    d2.Print();

    return 0;

}



在上述代码中,当  d1  调用  Init  函数时, Init  函数如何知道要设置  d1  对象,而不是  d2  对象呢?C++ 编译器为了解决这个问题,给每个“非静态的成员函数”增加了一个隐藏的指针参数  this 。它指向当前对象(即函数运行时调用该函数的对象),在函数体中对成员变量的操作都是通过这个指针来访问。也就是说,当  d1  调用  Init  函数时, this  指针指向  d1 ,函数通过  this  指针来操作  d1  的成员变量;当  d2  调用  Init  函数时, this  指针指向  d2  ,进而操作  d2  的成员变量。

9.2 this 指针的特性

1. 类型: this  指针的类型是“类类型* const” ,这意味着在成员函数中,不能给  this  指针赋值。例如,在成员函数内部写  this = nullptr;  这样的代码是不允许的,会导致编译错误。这是因为  this  指针的作用是指向当前对象,它的指向在函数调用时已经确定,不应该被随意修改。

2. 使用范围: this  指针只能在“成员函数”的内部使用。它是成员函数特有的一个隐含指针,在类的外部或者非成员函数中,是无法访问和使用  this  指针的。

3. 本质: this  指针本质上是“成员函数”的形参。当对象调用成员函数时,将对象的地址作为实参传递给  this  形参。所以对象中实际上并不存储  this  指针,它只是在成员函数调用过程中,作为一个隐含的参数存在,用于区分不同对象调用成员函数时的操作对象。

4. 传递方式: this  指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过  ecx  寄存器自动传递,不需要用户手动传递。例如:


cpp

class Date {

public:

    void Display() {

        std::cout << _year << std::endl;

    }

private:

    int _year;

};



编译器处理后,上述  Display  函数等价于:


 

cpp

class Date {

public:

    void Display(Date* const this) {

        std::cout << this->_year << std::endl;

    }

private:

    int _year;

};



这里可以看到,编译器自动为成员函数添加了  this  指针参数,并且在函数体中通过  this  指针来访问成员变量。

9.3 面试题相关分析

- this指针存在哪里?: this  指针本质是成员函数的形参,当对象调用成员函数时才会传递。它一般存放在寄存器(如  ecx  寄存器 )中,由编译器自动管理。因为它不是对象的成员,所以并不存在于对象的内存空间里。

- this指针可以为空吗?:从语法角度, this  指针可以为空。但如果在成员函数中直接通过空的  this  指针去访问成员变量,就会导致程序崩溃,因为空指针无法正确指向有效的内存地址来获取成员变量的值。不过,如果成员函数中不访问成员变量,只是执行一些不依赖对象数据的操作,那么即使  this  指针为空,函数也可以正常执行。例如:


 

cpp

class A {

public:

    void Print() {

        std::cout << "This is a function that doesn't access member variables." << std::endl;

    }

};



int main() {

    A* p = nullptr;

    p->Print(); // 这里虽然p为空,但Print函数不依赖成员变量,所以不会崩溃

    return 0;

}



十、C 语言和 C++ 实现栈的对比

10.1 C 语言实现栈

以下是用 C 语言实现栈的代码:


c

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>



// 定义数据类型别名

typedef int DataType;



// 定义栈结构体

typedef struct stack {

    DataType* array;

    int capacity;

    int size;

} Stack;



// 初始化栈

void StackInit(Stack* ps) {

    assert(ps);

    ps->array = (DataType*)malloc(sizeof(DataType) * 3);

    if (NULL == ps->array) {

        assert(0);

        return;

    }

    ps->capacity = 3;

    ps->size = 0;

}



// 销毁栈

void StackDestroy(Stack* ps) {

    assert(ps);

    if (ps->array) {

        free(ps->array);

        ps->array = NULL;

        ps->capacity = 0;

        ps->size = 0;

    }

}



// 检查栈容量,必要时扩容

void CheckCapacity(Stack* ps) {

    if (ps->size == ps->capacity) {

        int newcapacity = ps->capacity * 2;

        DataType* temp = (DataType*)realloc(ps->array, newcapacity * sizeof(DataType));

        if (temp == NULL) {

            perror("realloc申请空间失败!!!");

            return;

        }

        ps->array = temp;

        ps->capacity = newcapacity;

    }

}



// 入栈操作

void StackPush(Stack* ps, DataType data) {

    assert(ps);

    CheckCapacity(ps);

    ps->array[ps->size] = data;

    ps->size++;

}



// 判断栈是否为空

int StackEmpty(Stack* ps) {

    assert(ps);

    return 0 == ps->size;

}



// 出栈操作

void StackPop(Stack* ps) {

    if (StackEmpty(ps))

        return;

    ps->size--;

}



// 获取栈顶元素

DataType StackTop(Stack* ps) {

    assert(!StackEmpty(ps));

    return ps->array[ps->size - 1];

}



// 获取栈的大小

int StackSize(Stack* ps) {

    assert(ps);

    return ps->size;

}



int main() {

    Stack s;

    StackInit(&s);

    StackPush(&s, 1);

    StackPush(&s, 2);

    StackPush(&s, 3);

    StackPush(&s, 4);

    printf("%d\n", StackTop(&s));

    printf("%d\n", StackSize(&s));

    StackPop(&s);

    StackPop(&s);

    printf("%d\n", StackTop(&s));

    printf("%d\n", StackSize(&s));

    StackDestroy(&s);

    return 0;

}

在 C 语言实现中,栈相关操作函数具有以下共性:

- 每个函数的第一个参数都是  Stack*  类型,用于指向要操作的栈结构体实例。

- 函数中必须要对第一个参数(栈结构体指针)进行检测,因为该参数可能会为  NULL ,如果不检测直接使用可能会导致程序崩溃。

- 函数中都是通过  Stack  参数来操作栈的具体成员变量,如  array 、 capacity 、 size  等。

- 调用时必须传递  Stack  结构体变量的地址,因为函数需要通过指针来访问和修改栈的内部状态。

结构体中只能定义存放数据的结构,操作数据的方法不能放在结构体中,即数据和操作数据的方式是分离开的。这种实现方式涉及到大量指针操作,代码相对复杂,稍不注意就可能会出错,例如内存管理不当导致的内存泄漏等问题。

10.2 C++ 实现栈

以下是用 C++ 实现栈的代码:


cpp

#include <iostream>

#include <cassert>

#include <cstring>



// 定义数据类型别名

typedef int DataType;



class Stack {

public:

    // 初始化栈

    void Init() {

        _array = (DataType*)malloc(sizeof(DataType) * 3);

        if (NULL == _array) {

            perror("malloc申请空间失败!!!");

            return;

        }

        _capacity = 3;

        _size = 0;

    }



    // 入栈操作

    void Push(DataType data) {

        CheckCapacity();

        _array[_size] = data;

        _size++;

    }



    // 出栈操作

    void Pop() {

        if (Empty())

            return;

        _size--;

    }



    // 获取栈顶元素

    DataType Top() { return _array[_size - 1]; }



    // 判断栈是否为空

    int Empty() { return 0 == _size; }



    // 获取栈的大小

    int size() { return _size; }



    // 销毁栈

    void Destroy() {

        if (_array) {

            free(_array);

            _array = NULL;

            _capacity = 0;

            _size = 0;

        }

    }



private:

    // 检查栈容量,必要时扩容

    void CheckCapacity() {

        if (_size == _capacity) {

            int newcapacity = _capacity * 2;

            DataType* temp = (DataType*)realloc(_array, newcapacity * sizeof(DataType));

            if (temp == NULL) {

                perror("realloc申请空间失败!!!");

                return;

            }

            _array = temp;

            _capacity = newcapacity;

        }

    }



private:

    DataType* _array;

    int _capacity;

    int _size;

};



int main() {

    Stack s;

    s.Init();

    s.Push(1);

    s.Push(2);

    s.Push(3);

    s.Push(4);

    std::cout << s.Top() << std::endl;

    std::cout << s.size() << std::endl;

    s.Pop();

    s.Pop();

    std::cout << s.Top() << std::endl;

    std::cout << s.size() << std::endl;

    s.Destroy();

    return 0;

}



在 C++ 实现中,通过类将数据和操作封装在一起。类的成员函数可以直接访问类的私有成员变量,不需要像 C 语言那样通过结构体指针来显式传递和访问。例如, Push  函数中可以直接调用  CheckCapacity  函数和访问  _array 、 _size  等私有成员变量。这种方式语法更简洁,代码的可读性和可维护性更好,同时也更好地体现了面向对象编程中数据和操作相结合的思想,降低了因指针操作不当而产生错误的可能性。

十一、总结

本文全面且深入地介绍了 C++ 类与对象(上)的关键知识点。从面向过程与面向对象编程思想的差异出发,逐步深入到类的定义、访问限定符、封装特性、作用域、实例化过程、对象大小计算以及  this  指针等重要内容,并结合丰富且详细的代码示例进行讲解。同时,通过对比 C 语言和 C++ 对栈的实现,清晰地展现了 C++ 在面向对象编程方面的优势。

掌握这些基础知识是进一步学习 C++ 面向对象编程高级特性(如继承、多态等)的重要基石。希望读者通过本文能够对 C++ 类与对象有一个系统、清晰且深入的理解,为后续的编程学习和实践打下坚实的基础。

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

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

相关文章

Lumerical ------ Edge coupler design

Lumerical ------ Edge coupler design 引言正文无 Si Substrate 的仿真步骤有 Si Substrate 的仿真步骤引言 本文,我们将使用官方提供的 Edge coupler 设计教程,但是中间会带有作者本人的设计的感悟。 正文 无 Si Substrate 的仿真步骤 打开 Edge_Coupler_No_Substrate.l…

大语言模型本质上还是自动化,而不是智能化

大语言模型本质上仍然是自动化或高级自动化&#xff0c;而非真正的智能化&#xff0c;原因可以从以下几个方面进行分析&#xff1a;1、自动化与智能化的本质区别自动化&#xff1a;大语言模型通过预训练和微调&#xff0c;基于大量数据和规则生成输出。它的行为是基于输入数据的…

python数据结构——链表、栈、队列

一、思维梳理&#xff1a; 二、双向循环链表&#xff1a; class Node:def __init__(self,data):self.data dataself.next Noneself.prev Noneclass DoubleLink:def __init__(self):self.size 0self.head Nonedef is_empty(self):return self.size 0def add_end(self,dat…

centos操作系统如何更换yum镜像源

CentOS Linux 是一个免费提供的、社区支持的Linux发行版,由CentOS项目社区贡献者开发、分发和维护。2020年CentOS项目宣布将把全部投资转移到CentOS Stream,作为即将发布的 Red Hat Enterprise Linux版本的上游开发平台。因此,CentOS Linux更新和发布将在2021年至2024年期间…

【Linux篇】自主Shell命令行解释器

&#x1f4cc; 个人主页&#xff1a; 孙同学_ &#x1f527; 文章专栏&#xff1a;Liunx &#x1f4a1; 关注我&#xff0c;分享经验&#xff0c;助你少走弯路&#xff01; 文章目录 1. 获取用户名的接口2. 等待用户输入接口3. 将上述代码进行面向对象式的封装4. 命令行解析5.…

我的创作纪念日-一周年

目录 机缘 收获 日常 成就 憧憬 机缘 时光荏苒&#xff0c;转行计算机已经是第5个年头了。从Python入门&#xff0c;到C入土&#xff0c;兜兜转转&#xff0c;发现自己也只是初窥门径&#xff0c;习得皮毛。我从6年前开始潜水CSDN&#xff0c;学习各路大佬的技术经验&…

多线程代码案例 - 1

目录 单例模式 1. 饿汉模式 2. 懒汉模式 单例模式与多线程 问题1 问题2 问题3 完&#xff01; 单例模式 单例模式是一种设计模式。 设计模式&#xff0c;是我们在编写代码时候的一种软性的规定&#xff0c;也就是说&#xff0c;我们遵守设计模式&#xff0c;代码的下限…

开发体育赛事直播系统主播认证功能技术实现方案

该体育直播系统系统由东莞梦幻网络科技开发&#xff0c;使用 ThinkPHP 作为后端&#xff0c;Vue.js 作为 PC/H5 端框架&#xff0c;Java 和 Objective-C 分别用于安卓和 iOS 开发。 1、前端实现 (Vue.js) <template><div class"anchor-certification">…

国产三维CAD「皇冠CAD」在汽车零部件领域建模教程:刹车片

本教程深度融合三维皇冠CAD&#xff08;CrownCAD&#xff09;的MBD&#xff08;Model-Based Definition&#xff09;设计理念&#xff0c;通过参数化建模、智能约束管理、动态装配验证等功能&#xff0c;实现数据驱动设计&#xff0c;精准解决了汽车制动系统中精密制动组件的设…

SpringMvc获取请求数据

基本参数 RequestMapping("save5") ResponseBody public User save5(String name, int age) {User user new User();user.setName(name);user.setAge(age);return user; } 在url中将name与age进行编写&#xff0c;通过框架可以提取url中的name与age&#xff0c;这…

大语言模型开发框架——LangChain

什么是LangChain LangChain是一个开发由语言模型驱动的应用程序的框架&#xff0c;它提供了一套工具、组件和接口&#xff0c;可以简化构建高级语言模型应用程序的过程。利用LangChain可以使应用程序具备两个能力&#xff1a; 上下文感知 将语言模型与上下文&#xff08;提示…

机器学习的一百个概念(7)独热编码

前言 本文隶属于专栏《机器学习的一百个概念》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见[《机器学习的一百个概念》 ima 知识库 知识库广场搜索&…

从实用的角度聊聊Linux下文本编辑器VIM

本文从实用的角度聊聊Vim的常用命令。何为实用&#xff1f;我举个不实用的例子大家就明白了&#xff0c;用vim写代码。;) “vim是从 vi 发展出来的一个文本编辑器。代码补全、编译及错误跳转等方便编程的功能特别丰富&#xff0c;在程序员中被广泛使用&#xff0c;和Emacs并列成…

佳能imageRUNNER 2206N基本参数及管理员密码

基本参数&#xff1a; 产品类型 激光数码复合机 颜色类型 黑白 涵盖功能 复印/打印/扫描 速度类型 低速 最大原稿尺寸 A3 复印/打印方式 激光静电转印方式 感光材料 OPC 显影系统 干式单组分显影 定影…

社交类 APP 设计:打造高用户粘性的界面

在当今数字化时代&#xff0c;社交类APP已成为人们日常生活中不可或缺的一部分。然而&#xff0c;随着市场竞争的加剧&#xff0c;如何通过设计提升用户粘性成为社交类APP成功的关键。本文将从设计的关键要素、用户界面优化、功能创新、个性化体验以及持续优化等方面&#xff0…

数据编排与Dagster:解锁现代数据管理的核心工具

在数据驱动的时代&#xff0c;如何高效管理复杂的数据管道、确保数据质量并实现团队协作&#xff1f;本文深入探讨数据编排的核心概念&#xff0c;解析其与传统编排器的差异&#xff0c;并聚焦开源工具Dagster如何以“资产为中心”的理念革新数据开发流程&#xff0c;助力企业构…

Jmeter的压测使用

Jmeter基础功能回顾 一、创建Jmeter脚本 1、录制新建 &#xff08;1&#xff09;适用群体&#xff1a;初学者 2、手动创建 &#xff08;1&#xff09;需要了解Jmeter的常用组件 元件&#xff1a;多个类似功能组件的容器&#xff08;类似于类&#xff09; 各元件作用 组件…

kubernetes》》k8s》》Deployment》》ClusterIP、LoadBalancer、Ingress 内部访问、外边访问

Nginx部署 K8s 集群内外访问服务的方式 节点 Kubernetes 集群中的服务器&#xff08;指单台&#xff09; 集群 Kubernetes 管理的一组服务器的集合 边界路由器 为局域网和Internet路由数据包的路由器&#xff0c;执行防火墙保护局域网络 集群网络 遵循Kubernetes网络模型实现集…

Transformer 通关秘籍8:词向量如何表示近义词?

上一节已经完成了 token 到词向量的转换。那么&#xff0c;使用转换后的词嵌入向量便可以表示 token 之间的语义了吗&#xff1f;便可以表示两个单词是否是近义词&#xff0c;是否是反义词了吗&#xff1f; 是的。 接下来先通过一个例子&#xff0c;来直观地理解一下词嵌入向…

【MVC简介-产生原因、演变历史、核心思想、组成部分、使用场景】

MVC简介 产生原因&#xff1a; MVC&#xff08;Model-View-Controller&#xff09;模式诞生于20世纪70年代&#xff0c;由Trygve Reenskaug在施乐帕克研究中心&#xff08;Xerox PARC&#xff09;为Smalltalk语言设计&#xff0c;目的是解决图形用户界面&#xff08;GUI&…