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

C++ 入门四:类与对象 —— 面向对象编程的核心基石

一、类的定义

1. 类的基本形式

class 类名 {
public:       // 公有成员(类内外均可访问)数据类型 数据成员;       // 公有数据成员数据类型 成员函数(参数列表);  // 公有成员函数声明
protected:    // 保护成员(类内和派生类可访问)数据类型 保护数据成员;数据类型 保护成员函数(参数列表);
private:     // 私有成员(仅类内可访问)数据类型 私有数据成员;数据类型 成员函数(参数列表);  // 私有成员函数声明
};  // 类定义结束必须加分号

2. 详细说明

  • 成员分类:类包含两种成员:
    • 数据成员:描述类的属性,如学生类中的姓名年龄
    • 成员函数:描述类的行为,如学生类中的设置年龄获取成绩
  • 访问权限
    • public(公有):成员可在类外直接访问,例如对象名.公有成员
    • protected(保护):成员可在类内和派生类中访问,类外不可直接访问。
    • private(私有):仅类内成员函数或友元可访问,类外和派生类(除非友元)不可直接访问。
  • 成员函数定义
    • 类内定义:直接在类体中编写函数体,自动成为内联函数(隐式inline)。
    • 类外定义:使用类名::作用域限定符,例如:
      void 类名::成员函数(参数列表) { /* 函数体 */ }
      

3. 示例:定义学生类

class Student {
public:// 公有数据成员char name[20];  // 姓名int age;        // 年龄// 公有成员函数(类内定义)void set_score(float s) {  // 设置成绩(私有成员)score = s;}// 公有成员函数(类外定义)float get_score();  // 获取成绩声明protected:int grade;  // 年级(保护成员,派生类可访问)private:float score;  // 成绩(私有成员,仅类内访问)
};// 类外定义成员函数
float Student::get_score() {return score;  // 访问私有成员score
}

二、对象的定义与成员访问

1. 对象定义

  • 语法类名 对象名; 或 类名 对象名(参数列表);(调用带参构造函数)
  • 示例
    Student stu1;         // 定义对象stu1(调用默认构造函数)
    Student stu2("张三", 18);  // 假设存在带参构造函数
    

2. 成员访问

  • 公有成员:直接通过对象名.成员名访问。
    stu1.age = 18;       // 合法,age是公有成员
    strcpy(stu1.name, "李四"); // 合法
    
  • 私有 / 保护成员:通过公有成员函数间接访问。
    stu1.set_score(90.5); // 合法,调用公有函数设置私有成员score
    // stu1.score = 90.5;  // 非法,score是私有成员
    

三、构造函数:对象的初始化

在面向对象编程中,对象的初始化是一个关键环节。构造函数作为类的特殊成员函数,承担着初始化对象的重要职责。下面将从基础概念到高级用法逐步解析构造函数的核心知识。

1. 构造函数概述

语法规则
  • 函数名:必须与类名完全相同(包括大小写)。
  • 返回值:没有返回值(不能写void)。
  • 调用时机:创建对象时自动调用(栈上定义、堆上new操作、函数返回对象等场景)。
核心作用
  • 成员初始化:为对象的成员变量赋初始值(如数值类型设为默认值、指针指向有效内存)。
  • 资源分配:为对象分配运行所需资源(如动态内存、文件句柄、网络连接等)。
示例:基础构造函数
class Book {
public:// 构造函数:初始化书名和页数Book() {strcpy(title, "未命名");  // 初始化C风格字符串pages = 0;}
private:char title[50];int pages;
};int main() {Book novel;  // 创建对象时自动调用Book()构造函数return 0;
}

2. 构造函数分类

(1)无参构造函数(默认构造函数)
定义与特性
  • 无参数:函数括号内没有参数列表。
  • 编译器自动生成:若类中未定义任何构造函数,编译器会生成一个空的默认构造函数(成员变量初始化为默认值,如数值为 0、指针为nullptr)。
  • 手动定义的必要性:若定义了其他带参构造函数,编译器不再自动生成默认构造函数,需手动定义以支持无参创建对象。
示例:手动定义默认构造函数
class Student {
public:// 显式定义默认构造函数Student() {id = 0;name = "匿名";score = 0.0f;}
private:int id;std::string name;float score;
};Student stu;  // 调用默认构造函数,id=0,name="匿名",score=0.0f
(2)带参构造函数
定义与作用
  • 包含参数:通过参数为成员变量赋初始值,支持灵活初始化。
  • 参数作用域:参数名可与成员变量同名,通过this指针区分(this->成员变量)。
示例:通过参数初始化成员
class Circle {
public:// 带参构造函数:初始化半径和面积Circle(float r) {radius = r;  // 成员变量radius = 参数rarea = 3.14f * r * r;  // 计算初始面积}
private:float radius;float area;
};Circle c(5.0f);  // 创建半径5.0的圆,area自动计算为78.5
(3)初始化列表(Constructor Initializer List)
语法与格式
  • 位置:在构造函数参数列表后,使用:分隔,多个成员用逗号分隔。
  • 格式构造函数(参数列表) : 成员1(值1), 成员2(值2), ... { 函数体 }
核心优势
  • 效率更高:直接调用成员的构造函数(如类成员对象),避免先默认构造再赋值。
  • 必需场景:初始化const成员或引用成员(二者必须在定义时初始化)。
示例:初始化列表的使用
class Person {
public:// 普通成员与const成员的初始化Person(std::string n, int a) : name(n), age(a) { // 函数体可空,初始化在列表完成}// 引用成员必须通过初始化列表赋值Person(std::string n, int a, int& ref) : name(n), age(a), ref_num(ref) { }
private:std::string name;int age;const int ref_num;  // const引用成员,必须在初始化列表赋值
};// 初始化const成员的错误与正确写法对比
class ErrorDemo {const int value;
public:ErrorDemo() { value = 10; }  // 错误!const成员不能赋值
};class CorrectDemo {const int value;
public:CorrectDemo() : value(10) { }  // 正确!通过初始化列表赋值
};
(4)构造函数重载
重载规则
  • 函数名相同,但参数列表不同(参数个数、类型、顺序至少有一个不同)。
  • 返回值类型无关:不能通过返回值区分重载构造函数。
示例:多种初始化方式
class Vector {
public:// 无参构造:初始化零向量Vector() : x(0), y(0) {}// 单参数构造:二维向量(x=y)Vector(float val) : x(val), y(val) {}// 双参数构造:指定x和yVector(float x_val, float y_val) : x(x_val), y(y_val) {}
private:float x, y;
};// 调用不同构造函数
Vector v1;        // 无参构造,(0, 0)
Vector v2(5.0f);  // 单参构造,(5.0, 5.0)
Vector v3(3.0f, 4.0f);  // 双参构造,(3.0, 4.0)
C++14 简化写法:= default
  • 作用:显式让编译器生成默认构造函数,保持代码简洁。
  • 示例
    class Simple {
    public:Simple() = default;  // 等价于空的默认构造函数Simple(int x) : data(x) {}
    private:int data;
    };
    

3. 初始化 const 成员的强制要求

为什么必须用初始化列表?
  • const成员在声明后不能被赋值(只能初始化),而构造函数的函数体执行时,成员变量已完成定义,无法再对const成员赋值。
  • 初始化列表在成员变量定义时直接赋值,满足const的初始化要求。
示例:const 成员的正确初始化
class MathConstants {
public:// 初始化const成员PI和引用成员epsilon(引用必须初始化)MathConstants(float eps) : PI(3.14159f), epsilon(eps) {}
private:const float PI;  // 圆周率,固定值float& epsilon;  // 精度引用,必须在初始化列表赋值
};// 错误示例:试图在函数体中赋值const成员
class ErrorCase {const int value;
public:ErrorCase(int v) { value = v; }  // 编译错误!const成员不能赋值
};// 正确示例:通过初始化列表赋值
class CorrectCase {const int value;
public:CorrectCase(int v) : value(v) {}  // 正确
};

4. 构造函数的最佳实践

(1)统一使用初始化列表
  • 无论是否为const成员,优先在初始化列表中赋值,提升效率(尤其对类成员对象)。
(2)避免冗余初始化
  • 若成员变量无需特殊处理,可依赖编译器默认初始化(如std::string默认构造为空字符串)。
(3)处理动态资源
  • 在构造函数中分配资源(如new内存),并在析构函数中释放(确保资源配对)。
    class DynamicArray {
    public:DynamicArray(int size) : data(new int[size]), length(size) {}~DynamicArray() { delete[] data; }  // 析构函数释放内存
    private:int* data;int length;
    };
    

总结:构造函数核心知识点

特性说明
必需性创建对象时必须调用构造函数,编译器自动生成默认构造函数(无其他构造函数时)。
初始化方式普通成员可在函数体赋值,const成员和引用成员必须通过初始化列表初始化。
重载规则参数列表不同(个数、类型、顺序),支持灵活的对象创建方式。
资源管理构造函数分配资源,析构函数释放资源,确保内存安全。

通过合理设计构造函数,开发者能确保对象在创建时处于有效状态,为后续操作奠定基础。下一节将深入解析析构函数与对象生命周期管理,进一步理解 C++ 对象的完整生命周期。

四、析构函数:对象的清理

在 C++ 中,对象的生命周期管理至关重要。构造函数负责对象的初始化,而析构函数则承担着对象销毁时的清理工作,确保资源正确释放,避免内存泄漏。下面从基础概念到实战应用逐步解析析构函数的核心知识。

1. 析构函数概述

语法规则
  • 函数名:以~符号开头,后跟类名(与构造函数对应),例如~ClassName()
  • 参数与返回值:没有参数,也没有返回值(不能写void)。
  • 调用时机
    • 栈对象离开作用域时(如函数结束)。
    • 堆对象通过delete释放时(如delete p;)。
    • 程序结束时(全局对象和静态对象销毁)。
核心作用
  • 资源释放:释放构造函数或成员函数分配的资源(如动态内存new、文件句柄fopen、网络连接等)。
  • 数据清理:重置成员变量状态,避免无效引用。
与构造函数的关系
  • 配对使用:构造函数分配资源,析构函数释放资源,形成 “资源管理对”(RAII 模式的基础)。
  • 执行顺序:构造函数按 “基类→派生类” 顺序执行,析构函数按 “派生类→基类” 逆序执行(确保派生类资源先释放,基类后释放)。

2. 示例:释放动态内存(核心场景)

需求:管理动态数组的生命周期

当类中包含动态分配的内存(如new创建的数组),必须在析构函数中用delete释放,否则会导致内存泄漏。

代码实现
#include <iostream>
using namespace std;class DynamicArray {
private:int* data;   // 动态数组指针int size;    // 数组大小public:// 构造函数:分配内存并初始化DynamicArray(int s) : size(s) {data = new int[size];  // 分配size个int的内存空间for (int i = 0; i < size; i++) {data[i] = i;  // 初始化数组元素}cout << "构造函数:分配内存,地址=" << data << endl;}// 析构函数:释放动态内存~DynamicArray() {delete[] data;  // 释放数组内存(与new[]配对)data = nullptr; // 置空指针,避免野指针cout << "析构函数:释放内存,地址=" << data << endl;}
};int main() {// 栈对象:离开main作用域时自动调用析构函数{DynamicArray arr(5);  // 构造函数执行,分配内存}  // 作用域结束,析构函数自动调用// 堆对象:显式调用delete时触发析构函数DynamicArray* ptr = new DynamicArray(3);  // 构造函数执行delete ptr;  // 手动释放,析构函数调用return 0;
}
代码解释
  1. 构造函数

    • 接收参数size,使用new[]分配动态数组内存。
    • 初始化数组元素,输出内存地址以便观察。
  2. 析构函数

    • 使用delete[]释放动态数组(必须与new[]配对,单个对象用delete)。
    • 释放后将指针置为nullptr,防止后续误操作(野指针问题)。
  3. 调用场景

    • 栈对象arr在离开花括号作用域时,自动调用析构函数。
    • 堆对象ptr通过delete显式释放,触发析构函数。

3. 注意事项与进阶知识

(1)默认析构函数:编译器的 “隐形助手”
  • 自动生成条件:若用户未定义析构函数,编译器会生成一个默认析构函数(空函数)。
    class Simple {int value;  // 无自定义析构函数,编译器生成~Simple() {}
    };
    
  • 局限性:默认析构函数仅能释放非动态资源(如基本类型、标准库对象),对new分配的内存、文件句柄等无效,需手动定义析构函数。
(2)析构函数与继承:顺序至关重要
  • 基类与派生类的执行顺序

    1. 创建派生类对象时:先执行基类构造函数 → 再执行派生类构造函数。
    2. 销毁派生类对象时:先执行派生类析构函数 → 再执行基类析构函数(与构造顺序相反)。
    class Base {
    public:~Base() { cout << "Base析构" << endl; }
    };
    class Derived : public Base {
    public:~Derived() { cout << "Derived析构" << endl; }
    };int main() {Derived obj;  // 输出:Base构造 → Derived构造(假设存在构造函数)// 销毁时输出:Derived析构 → Base析构return 0;
    }
    
(3)析构函数不能重载
  • 原因:析构函数没有参数列表,无法通过参数区分不同版本,因此每个类最多有一个析构函数。
(4)析构函数与异常处理
  • 原则:析构函数中避免抛出异常,否则可能导致程序终止。
  • 处理方式:若必须处理异常,应在析构函数内部捕获并处理,而非抛出。
    ~DynamicArray() {try {delete[] data;} catch (...) {// 处理异常或记录日志}
    }
    

4. 最佳实践:资源管理的黄金法则

(1)RAII 模式(资源获取即初始化)
  • 核心思想:通过构造函数获取资源,析构函数释放资源,确保资源生命周期与对象绑定。
  • 典型应用
    • 动态内存:new/delete配对。
    • 文件操作:构造函数打开文件,析构函数关闭文件。
    class FileHandler {
    public:FileHandler(const char* path) {file = fopen(path, "r");  // 构造函数打开文件}~FileHandler() {if (file) fclose(file);  // 析构函数关闭文件}
    private:FILE* file;
    };
    
(2)避免手动管理资源:使用智能指针
  • C++11 引入std::unique_ptrstd::shared_ptr,自动管理动态内存,无需手动编写析构函数。
    #include <memory>
    class ModernArray {
    private:std::unique_ptr<int[]> data;  // 智能指针自动释放内存
    public:ModernArray(int size) : data(new int[size]) {}  // 无需析构函数
    };
    

总结:析构函数核心知识点

特性说明
语法特征~类名命名,无参数、无返回值,自动调用于对象销毁时。
核心作用释放动态资源(如new内存、文件句柄),防止内存泄漏。
默认行为未定义时编译器生成空析构函数,仅适用于无动态资源的类。
继承场景析构顺序与构造顺序相反(派生类→基类),确保资源正确释放。
最佳实践结合 RAII 模式,或使用智能指针简化资源管理,避免手动编写繁琐析构逻辑。

通过合理设计析构函数,开发者能有效管理对象生命周期,确保程序的稳定性和资源利用率。下一节将深入探讨this指针与静态成员,进一步理解类的内部机制。

五、this 指针:指向当前对象的 “隐形指针”

1. this 指针作用

  • 解决命名冲突:当成员变量与参数同名时,用this->成员名区分。
    class Person {
    private:char name[20];int age;
    public:void set_name(char* name) {strcpy(this->name, name);  // this->name是成员变量,name是参数}
    };
    

2. 隐含参数

  • 成员函数隐式包含this指针参数,调用时自动传递当前对象地址。
    Person p;
    p.set_name("Alice");  // 等价于 set_name(&p, "Alice")
    

3. 返回当前对象引用(链式调用)

class Counter {
private:int value;
public:Counter& add(int n) {value += n;return *this;  // 返回当前对象引用}
};Counter c;
c.add(10).add(20);  // 链式调用,等价于 c.add(10); c.add(20);

六、静态成员:类级别的共享数据与函数

1. 静态成员变量

  • 定义:用static修饰,属于类而非对象,所有对象共享。

  • 初始化:必须在类外初始化,格式为类型 类名::变量名 = 初始值;

    class Student {
    public:static int total_students;  // 声明静态成员变量
    };
    int Student::total_students = 0;  // 类外初始化
    
  • 访问方式

    • 通过类名:Student::total_students
    • 通过对象:stu1.total_students(需公有权限)

2. 静态成员函数

  • 特点:只能访问静态成员(变量 / 函数),无this指针。
  • 应用场景:统计类的对象数量。
    class Student {
    public:static int get_total() {  // 静态成员函数return total_students;}
    };
    

七、const 成员:保护数据不被修改

1. const 成员变量

  • 初始化:必须通过构造函数初始化列表赋值,不可修改。
    class Math {
    private:const float PI;  // const成员变量
    public:Math() : PI(3.14f) {}  // 初始化列表赋值
    };
    

2. const 成员函数(常成员函数)

  • 语法:在函数声明后加const,保证不修改成员变量。
    class Circle {
    private:float radius;
    public:float get_radius() const {  // 常成员函数return radius;  // 不可修改radius}
    };
    

3. const 对象

  • 定义const 类名 对象名;,只能调用常成员函数。
    const Circle c(5.0);
    c.get_radius();  // 合法,调用常成员函数
    // c.set_radius(6.0);  // 非法,set_radius非const函数
    

八、友元:打破访问权限的 “特权”

在 C++ 中,类的封装性通过public/protected/private严格控制成员访问,但有时需要允许特定的函数或类突破这种限制,直接访问私有成员。** 友元(Friend)** 机制就是为此设计的 “特权通道”,它允许非类成员函数或其他类的成员函数访问当前类的私有 / 保护成员。

1. 友元函数

友元函数是获得类访问特权的非成员函数,分为两种类型:全局非成员友元函数和其他类的成员友元函数。

(1)非成员友元函数
定义与声明
  • 作用:允许一个全局函数(不属于任何类)访问类的私有 / 保护成员。
  • 声明方式:在类体内用friend关键字声明函数原型,格式为:
    friend 返回值类型 函数名(参数列表);  
    
  • 关键特性:友元函数不是类的成员,无需通过对象调用,但可访问类的所有成员。
示例:访问银行账户余额(私有成员)
#include <iostream>  
using namespace std;  class BankAccount {  
private:  float balance;  // 私有成员:账户余额  public:  // 声明全局函数display_balance为友元  friend void display_balance(const BankAccount& acc);  // 构造函数初始化余额  BankAccount(float bal) : balance(bal) {}  
};  // 友元函数定义:可直接访问私有成员balance  
void display_balance(const BankAccount& acc) {  cout << "账户余额:" << acc.balance << " 元" << endl;  
}  int main() {  BankAccount account(10000.5f);  display_balance(account);  // 合法!友元函数访问私有成员  return 0;  
}  
代码解析
  • 友元声明friend void display_balance(...)在类内声明,赋予该函数访问私有成员balance的权限。
  • 参数传递:使用const引用传递对象,避免拷贝构造,提高效率并防止修改原始对象。
(2)其他类的成员友元函数
应用场景

当类 B 的某个成员函数需要访问类 A 的私有成员时,可将该成员函数声明为类 A 的友元。

声明方式
class A {  
private:  int private_data;  
public:  // 声明类B的成员函数B::friend_func为友元  friend void B::friend_func(A& obj);  
};  class B {  
public:  void friend_func(A& obj) {  obj.private_data = 100;  // 合法!访问A的私有成员  }  
};  
示例:类间协作访问私有成员
#include <iostream>  
using namespace std;  class Teacher;  // 前向声明,解决类依赖  class Student {  
private:  int student_id;  
public:  // 声明Teacher类的成员函数Teacher::view_id为友元  friend void Teacher::view_id(Student& stu);  Student(int id) : student_id(id) {}  
};  class Teacher {  
public:  void view_id(Student& stu) {  cout << "学生ID:" << stu.student_id << endl;  // 访问私有成员  }  
};  int main() {  Student stu(20230001);  Teacher teacher;  teacher.view_id(stu);  // 教师类成员函数访问学生类私有ID  return 0;  
}  
注意事项
  • 前向声明:若友元函数所属的类未定义,需提前声明(如class Teacher;),但不能访问其成员细节。
  • 单向特权:仅被声明的成员函数拥有访问权,类 B 的其他成员函数仍无权限。

2. 友元类

定义与声明
  • 作用:将整个类 B 声明为类 A 的友元,类 B 的所有成员函数均可访问类 A 的私有 / 保护成员。
  • 声明方式:在类 A 内用friend class B;声明类 B 为友元。
示例:友元类访问私有成员
#include <iostream>  
using namespace std;  class Library;  // 前向声明  class Book {  
private:  string title;  int page_count;  public:  Book(string t, int p) : title(t), page_count(p) {}  // 声明Library为友元类  friend class Library;  
};  class Library {  
public:  void display_book_info(Book& b) {  // 访问Book的私有成员  cout << "书名:" << b.title << ", 页数:" << b.page_count << endl;  }  
};  int main() {  Book book("C++ Primer", 1000);  Library lib;  lib.display_book_info(book);  // 合法!友元类成员函数访问私有成员  return 0;  
}  
友元类的特性
  1. 单向性

    • 若 A 是 B 的友元类,B 不一定是 A 的友元类,除非 A 也声明 B 为友元。
    class A { friend class B; };  // A允许B访问自己的私有成员  
    class B { friend class A; };  // 需额外声明,B才允许A访问自己的私有成员  
    
  2. 不可传递性

    • 若 B 是 A 的友元,C 是 B 的友元,C 并非自动成为 A 的友元。
  3. 破坏封装性

    • 友元类可访问所有私有成员,打破类的封装边界,需谨慎使用(仅在必要的类间协作时使用)。

3. 友元的优缺点与最佳实践

(1)核心优势
  • 灵活协作:允许类间高效交互,避免通过公有接口间接访问(如性能敏感场景)。
  • 保留封装性:仅对特定函数 / 类开放权限,而非完全公开私有成员。
(2)潜在风险
  • 耦合度增加:友元关系会强化类间依赖,修改一方可能影响另一方。
  • 调试难度:私有成员的访问点分散在友元函数 / 类中,难以追踪。
(3)使用建议
  1. 最小特权原则:优先声明单个友元函数而非整个友元类,缩小特权范围。
  2. 文档说明:在友元声明处注释说明原因,提高代码可读性。
  3. 避免滥用:仅在必要时使用(如运算符重载、类间数据共享),优先通过公有接口实现。

总结:友元核心知识点

类型定义声明方式访问权限
非成员友元函数全局函数,通过friend声明获得类私有成员访问权friend void func(类对象);可访问类的所有成员(含私有)
成员友元函数其他类的成员函数,声明后可访问当前类私有成员friend void 类B::func(类A对象);仅该成员函数拥有访问权
友元类整个类的所有成员函数可访问当前类私有成员friend class 类B;类 B 的所有成员函数均有访问权

友元机制是 C++ 封装性的补充,合理使用能在保持代码结构的同时实现高效交互。但需注意控制特权范围,避免过度依赖,确保代码的可维护性和安全性。下一章节将深入探讨拷贝构造函数与对象复制,理解对象创建的深层机制。

九、拷贝构造函数:对象的 “复制粘贴”

1. 自定义拷贝构造函数

  • 语法:参数为当前类的const引用,避免递归调用。
    class String {
    private:char* str;
    public:String(char* s) {  // 普通构造函数str = new char[strlen(s)+1];strcpy(str, s);}String(const String& obj) {  // 拷贝构造函数str = new char[strlen(obj.str)+1];strcpy(str, obj.str);  // 深拷贝,避免浅拷贝问题}~String() { delete[] str; }
    };
    

2. 默认拷贝构造函数

  • 自动生成:若未定义,编译器生成默认版本(浅拷贝),适用于无动态资源的类。
  • 风险:若类包含动态内存,默认拷贝会导致多个对象指向同一块内存,释放时崩溃。

3. 调用场景

  • 对象初始化:String s2 = s1; 或 String s2(s1);
  • 函数传参:void func(String obj); 调用时复制实参对象。
  • 函数返回:String func() { String s; return s; } 返回时复制对象。

十、总结:类与对象核心知识点

概念关键特性
类的定义封装数据与行为,通过public/protected/private控制访问权限。
对象初始化构造函数(含参数、初始化列表、重载),自动调用,初始化成员数据。
资源管理析构函数释放资源,避免内存泄漏,与构造函数成对出现。
数据共享静态成员(变量 / 函数)属于类,所有对象共享,类外初始化。
权限突破友元函数 / 类可访问私有成员,打破封装限制,需谨慎使用。
对象复制拷贝构造函数实现深拷贝,避免默认浅拷贝的内存问题。
类型安全const 成员保证数据不被修改,常对象仅能调用常成员函数。

通过以上知识点,我们掌握了 C++ 类与对象的核心机制,从封装数据到管理对象生命周期,再到灵活处理对象间的关系。后续将深入学习继承与多态,进一步体会面向对象编程的强大能力。

相关文章:

C++ 入门四:类与对象 —— 面向对象编程的核心基石

一、类的定义 1. 类的基本形式 class 类名 { public: // 公有成员&#xff08;类内外均可访问&#xff09;数据类型 数据成员; // 公有数据成员数据类型 成员函数(参数列表); // 公有成员函数声明 protected: // 保护成员&#xff08;类内和派生类可访问&…...

DeepSeek:穿透行业知识壁垒的搜索引擎攻防战

DeepSeek&#xff1a;穿透行业知识壁垒的搜索引擎攻防战 文 / 产业智能观察组&#xff08;人机协同创作&#xff09; 一、搜索引擎的"认知折叠"危机 2024年Q1数据显示&#xff0c;百度搜索结果前10页中&#xff0c;61.7%的内容存在"伪专业化"现象——看似…...

SQL 查询中涉及的表及其作用说明

SQL 查询中涉及的表及其作用说明&#xff1a; 涉及的数据库表 表名别名/用途关联关系dbo.s_orderSO&#xff08;主表&#xff09;存储订单主信息&#xff08;订单号、日期、客户等&#xff09;dbo.s_orderdetailSoD&#xff08;订单明细&#xff09;通过 billid SO.billid 关…...

数组 array

1、数组定义 是一种用于存储多个相同类型数据的存储模型。 2、数组格式 &#xff08;1&#xff09;数据类型[ ] 变量名&#xff08;比较常见这种格式&#xff09; 例如&#xff1a; int [ ] arr0&#xff0c;定义了一个int类型的数组&#xff0c;数组名是arr0&#xff1b; &am…...

Git 查看提交历史

Git作为最流行的版本控制工具&#xff0c;其提交历史管理是开发者日常工作的核心部分。无论是回溯代码变更、定位问题根源&#xff0c;还是进行版本回退&#xff0c;掌握Git提交历史的操作技巧都至关重要。本文将全面解析Git提交历史相关命令&#xff0c;助你成为版本管理高手&…...

电脑提示“找不到mfc140u.dll“的完整解决方案:从原因分析到彻底修复

当你启动某个软件或游戏时&#xff0c;突然遭遇"无法启动程序&#xff0c;因为计算机中丢失mfc140u.dll"的错误提示&#xff0c;这确实令人沮丧。mfc140u.dll是Microsoft Foundation Classes&#xff08;MFC&#xff09;库的重要组成部分&#xff0c;属于Visual C Re…...

windows安卓子系统wsa隐藏应用列表的安装激活使用

Windows 11 安卓子系统应用部署全攻略 windows安卓子系统wsa隐藏应用列表的安装激活使用|过检测核心前端 在 Windows 11 系统中&#xff0c;安卓子系统为用户带来了在电脑上运行安卓应用的便利。经过一系列的操作&#xff0c;我们已经完成了 Windows 11 安卓子系统的底层和前端…...

深入探索 PyTorch:回归与分类模型的全方位解析

深入探索 PyTorch&#xff1a;回归与分类模型的全方位解析 在当今数据驱动的时代&#xff0c;机器学习与深度学习技术正广泛应用于各个领域&#xff0c;助力我们从海量数据中挖掘有价值的信息。而 PyTorch 作为一款备受青睐的深度学习框架&#xff0c;为开发者们提供了简洁且高…...

案例分析:东华新径,拉动式生产的智造之路

目录 文章目录 目录南京东华智能转向系统有限公司是一家什么公司&#xff1f;背景知识&#xff1a;新能源汽车生产制造流程简介东华遇见了什么问题&#xff1f;东华希望如何解决&#xff1f;解决思路&#xff1a;从 “推动式生产” 到 “拉动式生产”&#xff0c;从 “冗余式思…...

【android bluetooth 框架分析 01】【关键线程 5】【bt_main_thread介绍】

1. 概述 system/stack/btu/btu_task.cc bt_main_thread 是 Android Bluetooth 协议栈中的核心线程&#xff0c;负责处理蓝牙协议栈中的大部分关键任务和事件。它相当于蓝牙协议栈的"大脑"&#xff0c;协调各种蓝牙功能的运行。 2. 重要性 bt_main_thread 的重要性…...

城市应急安防系统EasyCVR视频融合平台:如何实现多源视频资源高效汇聚与应急指挥协同

一、方案背景 1&#xff09;项目背景 在当今数字化时代&#xff0c;随着信息技术的飞速发展&#xff0c;视频监控和应急指挥系统在公共安全、城市应急等领域的重要性日益凸显。尤其是在关键场所&#xff0c;高效的视频资源整合与传输能力对于应对突发公共事件、实现快速精准的…...

自动化测试常用函数

自动化测试常用函数 前言1. 元素的定位1.1 cssSelector1.2 xpath1.2.1 获取 HTML 页面所有的节点1.2.2 获取 HTML 页面指定的节点1.2.3 获取一个节点中的直接子节点1.2.4 获取一个节点的父节点1.2.5 实现节点属性的匹配1.2.6 使用指定索引的方式获取对应的节点内容 2. 操作测试…...

OpengGL教程(七)---摄像机

本章参考官方教程&#xff1a;摄像机 本系列历史文 OpengGL教程(一)—OpenGL环境的配置(GLFW3,GLAD) OpengGL教程(二)—渲染一个简单的窗体 OpengGL教程(三)—使用VAO和VBO方式绘制三角形 OpengGL教程(四)—使用EBO方式绘制矩形 OpengGL教程(五)—纹理的应用 OpengGL教程(六)—…...

springboot禁用静态资源

使用 application.properties 在 application.properties 中添加如下配置&#xff1a; spring.web.resources.add-mappingsfalse使用 application.yml 在 application.yml 中添加如下配置&#xff1a; spring:web:resources:add-mappings: false配置解释 spring.web.resou…...

猫咪如厕检测与分类识别系统系列【二】多图上传及猫咪分类特征提取更新

前情提要 家里养了三只猫咪&#xff0c;其中一只布偶猫经常出入厕所。但因为平时忙于学业&#xff0c;没法时刻关注牠的行为。我知道猫咪的如厕频率和时长与健康状况密切相关&#xff0c;频繁如厕可能是泌尿问题&#xff0c;停留过久也可能是便秘或不适。为了更科学地了解牠的…...

‌ViewModel和AndroidViewModel的主要区别

‌ViewModel和AndroidViewModel的主要区别在于它们的继承关系、构造函数以及使用场景。‌ ‌继承关系与构造函数‌&#xff1a; ‌ViewModel‌&#xff1a;ViewModel是一个抽象类&#xff0c;位于androidx.lifecycle包中。它是所有ViewModel类的基类&#xff0c;构造函数较为…...

myeclise导入项目并运行

1、把项目复制到myeclise工作目录 2、导入项目&#xff1a;将项目导入到myeclise工作目录 3、配置jre 4、把项目放到tomcat 5、运行项目 配置数据库后运行...

XSS 防御转义规则笔记

一、转义规则概述 核心目标&#xff1a;防止用户输入被浏览器解析为可执行代码&#xff0c;确保输入始终被视为数据而非代码。 关键策略&#xff1a;根据数据嵌入的上下文环境&#xff08;HTML、JavaScript、CSS 等&#xff09;&#xff0c;对特殊字符进行转义或编码。 二、不…...

【2025年认证杯数学中国数学建模网络挑战赛】C题 完整论文 全三问模型+求解+代码

目录 【2025年认证杯数学建模挑战赛】C题数据预处理与全三问求解一、问题重述二、模型假设与符号说明2.1 模型基本假设2.2 符号说明 三、数据预处理及分析四、问题一五、问题二5.1 基于互相关函数的反应延时识别5.2 反应过程延时结果分析5.3 基于BP神经网络的不合格产物预测5.4…...

iOS应用开发指南

开发一款iOS应用是一个系统化的过程&#xff0c;涵盖从环境搭建、界面设计、编码实现到测试发布的各个环节。以下是一份面向初学者的iOS移动应用开发指南&#xff0c;帮助你从零开始构建自己的App。 一、准备工作&#xff1a;开发环境与工具 必备设备 Mac电脑&#xff1a;iO…...

小刚说C语言刷题——第21讲 一维数组

在日常生活中&#xff0c;我们经常输入一组数据。例如输入一个班30名学生的语文成绩&#xff0c;或者输入一组商品的价格。这个时候&#xff0c;我们如何输入一组类型相同的数据呢&#xff1f;这里我们就要用到数组。 1.数组的概念 所谓数组就是一组相同类型数据的集合。数组中…...

苍穹外卖2

根据id查询员工 调用顺序&#xff1a;Controller—>Service—>ServiceImpl—>Mapper—>xml 1.controller层一个tab秒了&#xff08;ai生成&#xff09; 由于result泛型中希望返回一个employee对象&#xff0c;所以定义一个employee来接受Service的getbyid方法,在…...

C语言之双层for循环

一、第一小题引入 循环次数用 外层循环内层循环 这一题即是: 3412(次) 外循环一次 内循环一趟(可以形象理解为 时针和 分针) 运行结果如下: 一、第二小题引入 请阅读下列代码: 执行程序.程序执行的循环次数为多少? 答案:20次 这一题 外层循环 从5开始到i结束 中间的i是…...

第8课:多智能体系统评估与迭代

多智能体系统评估与迭代:从指标设计到持续优化的全流程指南 一、引言:当智能体协作出现“磨合期”:评估与迭代为何是必经之路? 在多智能体系统(MAS)的实际运行中,即使架构设计合理,也可能面临“协作效率下降”“资源浪费”等问题: 任务完成率突然从95%降至70%,却找…...

HTTP:三.HTTP报文

报文流 http报文是以一种类似的流的方式来发送数据的,所以报文流讲述了http报文的一些客观状态,相关术语:流入、流出形容事务处理。http报文任何时候是从上游向下游流入的!其中进过的节点既可能是上游,有可能是下游,如果从某个节点流出,那么相对于此节点流入的那个节点…...

.NET MAUI教程1-入门并发布apk包安装到真机

由于本人水平有限&#xff0c;如有写得不对的地方往指出&#xff0c;由于是使用公司的电脑&#xff0c;电脑的操作系统是英文的&#xff0c;没有权限修改&#xff0c;所以本文截图中的vs是英文版的 以发布android为例进行讲解 测试环境&#xff1a; window 11 vs2022 步骤如…...

你所拨打的电话是空号?手机状态查询API

一、引言 在当今数字化营销时代&#xff0c;电话销售仍然是许多企业获取客户的重要手段之一。然而&#xff0c;电销过程中常常遇到空号、风险号和沉默号等问题&#xff0c;这不仅降低了营销效率&#xff0c;还增加了企业的运营成本。例如&#xff0c;频繁拨打空号浪费了大量时…...

C++顺序栈的实现

顺序栈详细介绍 定义与特点 顺序栈&#xff08;Sequential Stack&#xff09;是一种基于数组实现的栈结构&#xff0c;利用数组的连续内存空间存储元素&#xff0c;遵循后进先出&#xff08;LIFO&#xff09;原则。其核心特点包括&#xff1a; 固定或动态容量&#xff1a;初始…...

element-ui 中的 select 组件如何 remote-method 函数中传参

在 select 组件中我们使用其 change 事件可以传参&#xff0c;请查看&#xff1a;el-select 中change 事件传参问题。 在我们使用 select 组件的远程搜索时&#xff0c;我们如何给 remote-method 这个方法添加自定义参数呢&#xff1f; 代码实现如下&#xff1a; <el-sel…...

浅谈解释型语言的运用

不得不说&#xff0c;程序不需要编译&#xff0c;程序在运行时才翻译成机器语言&#xff0c;每执行一次&#xff0c;都要翻译一次&#xff0c;因此效率比较低。在运行程序时才翻译&#xff0c;专门有一个解释器去进行翻译&#xff0c;每个语句都是执行的时候才翻译&#xff0c;…...

云手机哪个平台最好用?云手机性能、服务、技术等多维度测评分析

在云手机市场日渐繁荣的当下&#xff0c;面对琳琅满目的云手机平台&#xff0c;用户往往难以抉择云手机哪个平台最好。下面我们就从从性能、价格等多维度分析&#xff0c;云手机平台哪家强&#xff0c;队国内好用的云手机平台进行排行盘点。 好用的云手机平台排行盘点 第一&…...

idea版的cursor:Windsurf Wave 7

在企业环境中&#xff0c;Visual Studio Code和JetBrains系列是最常用的开发工具&#xff0c;覆盖了全球绝大多数开发者。这两类IDE各有优势&#xff0c;但JetBrains系列凭借其针对特定语言和企业场景的深度优化&#xff0c;使得用户很难轻易更换工具。 虽然Windsurf编辑器是基…...

【IDEA】创建 SpringBoot 项目连接 MySQL

前言 IntelliJ IDEA 作为一款专业的 Java 开发工具&#xff0c;在创建和管理 Spring Boot 项目方面具有显著优势。它深度集成了 Spring Boot 的开发流程&#xff0c;从项目初始化到最终部署都提供了高效的支持。IDEA 内置的 Spring Initializr 工具让项目创建变得极其简单&…...

使用 IntelliJ IDEA 进行远程调试

1. 前言 今天线上出现了个 Bug &#xff0c;而且比较坑的是涉及到k8s环境相关的东西不能线下调试。传统方式是在代码中各种的日志 log 埋点然后重新部署进行调试&#xff0c;再根据 log 中的信息进行分析。如果你的 log 埋点不合理&#xff0c;就要不停的修改代码、不停的打包…...

Linux操作系统--进程状态

目录 1.运行、阻塞、挂起 1.1运行 1.2阻塞 1.3挂起(了解一下即可&#xff0c;基本不会出现这种情况) 2.进程状态 2.1进程状态查看 2.2 Z-僵尸进程 2.3孤儿进程 1.运行、阻塞、挂起 1.1运行 运行状态&#xff1a; 进程正在占用CPU执行指令。此时进程对系统资源&#xff…...

【端到端】端到端自动驾驶依赖Occupancy进行运动规划?还是可以具有生成局部地图来规划?

端到端自动驾驶系统的架构设计&#xff0c;目前主流做法实际上已经出现两种路径&#xff0c;我们来拆解一下&#xff1a; &#x1f698; 一、Occupancy 是否用于运动规划&#xff1f; 一种趋势是使用 Occupancy 表示作为 中间表征&#xff0c;用于&#xff1a; 运动规划&…...

HarmonyOS-ArkUIV2装饰器-@Param:组件外部输入

上文我们了解了@Local装饰器 ,讲明了Local装饰器不允许外部传入值对其进行初始化。详见: HarmonyOS-ArkUI V2装饰器@Local装饰器:组件内部状态-CSDN博客。 但总有场景是需要外部组件传值过来,然后本组件接收这个值这种场景的。而且很多情况下,一个状态变量的作用范围会是…...

报错 ImportError: cannot import name ‘packaging‘ from ‘pkg_resources‘

解决方法一 &#xff08;推荐&#xff09; 先检查setuptools是否为70.0.0版本 pip list | grep setuptools再降低版本 python -m pip install setuptools69.5.1或者&#xff1a; 解决方法二 上述的报错信息表明&#xff1a;在安装 mmcv 时出现了 pkg_resources 模块的问题…...

Ollama调用多GPU实现负载均衡

文章目录 &#x1f4ca; 背景说明&#x1f6e0;️ 修改 systemd 服务配置1. 配置文件路径2. 编辑服务文件2. 重新加载配置并重启服务3. 验证配置是否成功 &#x1f4c8; 应用效果示例1. 调用单个70b模型2. 调用多个模型&#xff08;70b和32b模型&#xff09; 总结&#x1f4cc;…...

Next.js 简介

Next.js 是一个由 Vercel 开发的基于 React 的 Web 开发框架&#xff0c;旨在简化 React 应用的开发流程&#xff0c;提供更好的性能和开发体验。 &#x1f31f; Next.js 的核心特点 1. 文件系统路由&#xff08;File-system Routing&#xff09; 在 pages/ 目录中创建文件就…...

使用Apache POI(Java)创建docx文档和表格

1、引入poi 依赖组件 <dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>4.0.0</version> </dependency> <dependency><groupId>org.apache.poi</groupId>&…...

Dynamics365 ExportPdfTemplateExportWordTemplate两个Action调用的body构造

这两天在用ExportPdfTemplate做pdf导出功能时&#xff0c;遇到了如下问题InnerException : Microsoft.OData.ODataException: An unexpected StartArray node was found when reading from the JSON reader. A PrimitiveValue node was expected. 我的场景是使用power automate…...

《算法笔记》3.3小节——入门模拟->图形输出

1036 跟奥巴马一起编程 #include <iostream> #include <cmath> using namespace std;int main() {int n,m;char c;cin>>n>>c;for (int i 0; i < n; i) {cout<<c;}cout<<endl;m round(1.0*n/2)-2;//round里面不能直接写n/2&#xff0c;…...

iOS 上的内存管理是如何处理的?

iOS主要通过自动引用计数&#xff08;ARC&#xff09;和内存管理模型来处理内存。以下是对这两者的详细介绍以及在实际工作中的应用场景&#xff1a; 1. 自动引用计数&#xff08;ARC&#xff09; ARC是iOS和macOS中的内存管理机制。它能自动跟踪和管理应用程序的内存使用情况…...

河北工程大学e2e平台,python

题目&#xff0c;选择题包100分&#xff01; 题目&#xff0c;选择题包100分&#xff01; 题目&#xff0c;选择题包100分&#xff01; 联系&#x1f6f0;&#xff1a;18039589633...

【Qt】spdlog日志模块的使用

版本 spdlog版本&#xff1a;1.5.0 采用1.5.0版本主要基于以下考虑&#xff1a;兼容Qt5.9.X版本和兼容C11。 spdlog 1.5.0下载地址&#xff1a;https://github.com/gabime/spdlog/releases/tag/v1.5.0 摘要 在Qt应用程序开发中&#xff0c;良好的日志系统至关重要。本文将介…...

python相关面试题

python相关面试题 1.上下文管理器需要实现哪两种方法以及相关应用 2.对比一下进程&#xff0c;线程和协程 3.魔法函数有哪些 4.什么是闭包和装饰器&#xff0c;装饰器缺点是什么 5.什么是浅拷贝和深拷贝 6.什么是GIL锁和互斥锁 7.init和new有什么区别&#xff0c;new方法的返回…...

swift菜鸟教程11-12(数组与字典)

一个朴实无华的目录 今日学习内容&#xff1a;1.Swift 数组1.1创建数组1.2访问数组1.3修改数组使用 append() 方法或者赋值运算符 在数组末尾添加元素通过索引修改数组元素的值&#xff1a; 1.4遍历数组 使用for-in循环同时需要每个数据项的值和索引值 1.5合并数组1.6count 属…...

.NET WPF 控件类分层结构

.NET WPF 控件类分层结构 在 WPF 中&#xff0c;类的层级结构设计是为了实现线程安全、依赖属性、可视化渲染、布局和交互等功能。以下是 WPF 核心基类的逐级说明&#xff0c;从最底层到最顶层&#xff1a; 1 DispatcherObject 作用&#xff1a; 提供与 WPF 线程模型&#xf…...

基于ImGui+FFmpeg实现播放器

基于ImGuiFFmpeg实现播放器 演示&#xff1a; ImGui播放器 继续研究FFmpeg&#xff0c;之前做了一个SDL的播放器&#xff0c;发现SDL的可视化UI界面的功能稍微差了点&#xff0c;所以今天我们换了一个新的工具&#xff0c;也就是ImGui。 ImGui官方文档&#xff1a;https://g…...