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++ 类与对象的核心机制,从封装数据到管理对象生命周期,再到灵活处理对象间的关系。后续将深入学习继承与多态,进一步体会面向对象编程的强大能力。

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

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

相关文章

嵌入式---电机分类

一、按电流类型分类&#xff08;最基础分类&#xff09; 1. 直流电机&#xff08;DC Motor&#xff09; 工作原理&#xff1a;通过换向器&#xff08;有刷&#xff09;或电子换向&#xff08;无刷&#xff09;将直流电源转换为交变磁场&#xff0c;驱动转子旋转。 核心特点&a…

【python】并行编程模块:threading / mutliprocess / parallel / Celery

在并行编程中&#xff0c;Python 具有简化实现的内置和外部模块。 本书是基于Python3.X的。 Python的threading模块 Python的threading模块为模块 _thread 提供了一个抽象层&#xff0c;它是一个较低级别的模块。 它提供的功能可以帮助程序员完成基于线程开发并行系统的艰巨任…

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

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

安卓手机怎样开启双WiFi加速

1. 小米/Redmi手机 路径&#xff1a; 设置 → WLAN → 高级设置 → 双WLAN加速 操作&#xff1a; 开启功能后&#xff0c;可同时连接一个2.4GHz WiFi和一个5GHz WiFi&#xff08;或两个不同路由器&#xff09;。 可选择“智能选择”或手动指定辅助网络。 2. 华为/荣耀手机…

什么是八步工作法?

八步工作法&#xff0c;顾名思义&#xff0c;就是把一项工作拆分成八个步骤来完成。它的核心目的是让工作变得更有条理&#xff0c;更高效&#xff0c;避免忙而无序&#xff0c;做到事事有着落&#xff0c;件件有结果。这个方法在很多企业和单位中都有应用&#xff0c;尤其适合…

前端Node.js的包管理工具npm指令

‌npm&#xff08;Node Package Manager&#xff09;是Node.js的包管理工具&#xff0c;主要用于安装、更新、删除和管理JavaScript包。以下是前端开发中常用的npm命令及其用途‌&#xff1a; 基本命令 npm提供了一系列命令行工具&#xff0c;用于执行各种包管理操作。以下是一…

掌握C语言文件操作:从理论到实战指南

文件操作是C语言编程中不可或缺的一部分&#xff0c;它使得程序能够持久化存储数据&#xff0c;并在需要时高效读写。本文将从基础概念到实战技巧&#xff0c;系统讲解C语言文件操作的核心知识点&#xff0c;并结合代码示例帮助读者深入理解。 一. 为什么需要文件操作&#xf…

Linux 线程:从零构建多线程应用:系统化解析线程API与底层设计逻辑

线程 线程的概述 在之前&#xff0c;我们常把进程定义为 程序执行的实例&#xff0c;实际不然&#xff0c;进程实际上只是维护应用程序的各种资源&#xff0c;并不执行什么。真正执行具体任务的是线程。 那为什么之前直接执行a.out的时候&#xff0c;没有这种感受呢&#xf…

014_多线程

多线程 多线程创建线程方式一&#xff1a;继承Thread类方式二&#xff1a;实现Runable接口方式三&#xff1a;实现Callbale接口 Thread的常用方法线程安全线程同步方式一&#xff1a;同步代码块同步方法方式三&#xff1a;Lock锁 线性池创建线程池处理Runnable任务处理Callable…

机场跑道异物检测数据集VOC+YOLO格式33793张31类别

数据集分辨率都是300x300,都是贴近地面拍摄&#xff0c;具体看图片 据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;33793 标注数量(xml文件…

Spring Cloud 远程调用

4.OpenFeign的实现原理是什么&#xff1f; 在使用OpenFeign的时候&#xff0c;主要关心两个注解&#xff0c;EnableFeignClients和FeignClient。整体的流程分为以下几个部分&#xff1a; 启用Feign代理&#xff0c;通过在启动类上添加EnableFeignClients注解&#xff0c;开启F…

Unity中使用FMETP STREAM传输实时画面

一、客户端&#xff08;发送端&#xff09; 总体思路&#xff1a;先把画面编码Encoder&#xff0c;再发送给服务端 新建场景&#xff0c;创建一个实体&#xff0c;名为FMnet&#xff0c;添加组件FMNetworkManager&#xff0c;将NetworkType设置为客户端Client&#xff0c;设置…

Baklib三步构建企业内容中台

需求调研构建内容中台 企业内容中台建设的首要环节在于精准识别业务需求与知识管理痛点。通过Baklib 是什么类型的工具的定位分析可知&#xff0c;其作为知识管理中枢&#xff0c;能够系统梳理客户服务场景中的高频咨询、产品文档更新需求及跨部门协作流程。在需求调研阶段&am…

实现抗隐私泄漏的AI人工智能推理

目录 什么是私人AI? 什么是可信执行环境? TEE 如何在 AI 推理期间保护数据? 使用 TEE 是否存在风险? 有哪些风险? Atoma 如何应对这些风险 为什么去中心化网络是解决方案 人工智能推理过程中还有其他保护隐私的方法吗? 私人人工智能可以实现什么? 隐私驱动的应…

一、TorchRec里边的输入输出类型

TorchRec中的输入和输出格式 文章目录 TorchRec中的输入和输出格式前言一、JaggedTensor1.1 核心概念1.2 核心属性&#xff0c;也就是参数1.3 关键操作与方法 二、KeyedJaggedTensor2.1 核心概念2.2 核心属性&#xff0c;也就是参数 3、KeyedTensor总结 前言 TorchRec具有其特…

JAVA实现在H5页面中点击链接直接进入微信小程序

在普通的Html5页面中如何实现点击URL链接直接进入微信小程序&#xff0c;不需要扫描小程序二维码&#xff1f; 网上介绍的很多方法是在小程序后台设置Schema&#xff0c;不过我进入我的小程序后台在开发设置里面 没有找到设置小程序Schema的地方&#xff0c;我是通过调用API接口…

uniapp解决上架华为应用市场审核要求-监听权限的申请

支持android平台全局监听权限的申请。当申请权限时&#xff0c;会在页面顶部显示申请权限的目的。主要解决上架华为应用市场审核要求&#xff1a;APP在调用终端权限时&#xff0c;应同步告知用户申请该权限的目的。 因为如果不提示&#xff0c;你上架应用市场会被打打回来 Tip…

文件IO5(JPEG图像原理与应用)

JPEG图像原理与应用 ⦁ 基本概念 JPEG&#xff08;Joint Photographic Experts Group&#xff09;指的是联合图像专家组&#xff0c;是国际标准化组织ISO制订并于1992年发布的一种面向连续色调静止图像的压缩编码标准&#xff0c;所以也被称为JPEG标准。 同样&#xff0c;JP…

vue3 history路由模式刷新页面报错问题解决

在使用history路由模式时刷新网页提示404错误&#xff0c;这是改怎么办呢。 官方解决办法 https://router.vuejs.org/zh/guide/essentials/history-mode.html

3D激光轮廓仪知识整理(待完善)

文章目录 1.原理和应用场景1.1 相机原理1.1.1 测量原理1.1.2 相机激光器1.1.3 沙姆镜头1.1.4 相机标定1.1.5 中心线提取 1.2 应用场景1.2.1 测量相关应用1.2.2 缺陷检测相关应用 2.相机参数介绍及选型介绍2.1 成像原理2.2 原始图成像2.3 生成轮廓图2.4 相机规格参数2.4.1 单轮廓…