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;
}
代码解释
-
构造函数:
- 接收参数
size
,使用new[]
分配动态数组内存。 - 初始化数组元素,输出内存地址以便观察。
- 接收参数
-
析构函数:
- 使用
delete[]
释放动态数组(必须与new[]
配对,单个对象用delete
)。 - 释放后将指针置为
nullptr
,防止后续误操作(野指针问题)。
- 使用
-
调用场景:
- 栈对象
arr
在离开花括号作用域时,自动调用析构函数。 - 堆对象
ptr
通过delete
显式释放,触发析构函数。
- 栈对象
3. 注意事项与进阶知识
(1)默认析构函数:编译器的 “隐形助手”
- 自动生成条件:若用户未定义析构函数,编译器会生成一个默认析构函数(空函数)。
class Simple {int value; // 无自定义析构函数,编译器生成~Simple() {} };
- 局限性:默认析构函数仅能释放非动态资源(如基本类型、标准库对象),对
new
分配的内存、文件句柄等无效,需手动定义析构函数。
(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_ptr
和std::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;
}
友元类的特性
-
单向性:
- 若 A 是 B 的友元类,B 不一定是 A 的友元类,除非 A 也声明 B 为友元。
class A { friend class B; }; // A允许B访问自己的私有成员 class B { friend class A; }; // 需额外声明,B才允许A访问自己的私有成员
-
不可传递性:
- 若 B 是 A 的友元,C 是 B 的友元,C 并非自动成为 A 的友元。
-
破坏封装性:
- 友元类可访问所有私有成员,打破类的封装边界,需谨慎使用(仅在必要的类间协作时使用)。
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: // 公有成员(类内外均可访问)数据类型 数据成员; // 公有数据成员数据类型 成员函数(参数列表); // 公有成员函数声明 protected: // 保护成员(类内和派生类可访问&…...
DeepSeek:穿透行业知识壁垒的搜索引擎攻防战
DeepSeek:穿透行业知识壁垒的搜索引擎攻防战 文 / 产业智能观察组(人机协同创作) 一、搜索引擎的"认知折叠"危机 2024年Q1数据显示,百度搜索结果前10页中,61.7%的内容存在"伪专业化"现象——看似…...
SQL 查询中涉及的表及其作用说明
SQL 查询中涉及的表及其作用说明: 涉及的数据库表 表名别名/用途关联关系dbo.s_orderSO(主表)存储订单主信息(订单号、日期、客户等)dbo.s_orderdetailSoD(订单明细)通过 billid SO.billid 关…...
数组 array
1、数组定义 是一种用于存储多个相同类型数据的存储模型。 2、数组格式 (1)数据类型[ ] 变量名(比较常见这种格式) 例如: int [ ] arr0,定义了一个int类型的数组,数组名是arr0; &am…...
Git 查看提交历史
Git作为最流行的版本控制工具,其提交历史管理是开发者日常工作的核心部分。无论是回溯代码变更、定位问题根源,还是进行版本回退,掌握Git提交历史的操作技巧都至关重要。本文将全面解析Git提交历史相关命令,助你成为版本管理高手&…...
电脑提示“找不到mfc140u.dll“的完整解决方案:从原因分析到彻底修复
当你启动某个软件或游戏时,突然遭遇"无法启动程序,因为计算机中丢失mfc140u.dll"的错误提示,这确实令人沮丧。mfc140u.dll是Microsoft Foundation Classes(MFC)库的重要组成部分,属于Visual C Re…...
windows安卓子系统wsa隐藏应用列表的安装激活使用
Windows 11 安卓子系统应用部署全攻略 windows安卓子系统wsa隐藏应用列表的安装激活使用|过检测核心前端 在 Windows 11 系统中,安卓子系统为用户带来了在电脑上运行安卓应用的便利。经过一系列的操作,我们已经完成了 Windows 11 安卓子系统的底层和前端…...
深入探索 PyTorch:回归与分类模型的全方位解析
深入探索 PyTorch:回归与分类模型的全方位解析 在当今数据驱动的时代,机器学习与深度学习技术正广泛应用于各个领域,助力我们从海量数据中挖掘有价值的信息。而 PyTorch 作为一款备受青睐的深度学习框架,为开发者们提供了简洁且高…...
案例分析:东华新径,拉动式生产的智造之路
目录 文章目录 目录南京东华智能转向系统有限公司是一家什么公司?背景知识:新能源汽车生产制造流程简介东华遇见了什么问题?东华希望如何解决?解决思路:从 “推动式生产” 到 “拉动式生产”,从 “冗余式思…...
【android bluetooth 框架分析 01】【关键线程 5】【bt_main_thread介绍】
1. 概述 system/stack/btu/btu_task.cc bt_main_thread 是 Android Bluetooth 协议栈中的核心线程,负责处理蓝牙协议栈中的大部分关键任务和事件。它相当于蓝牙协议栈的"大脑",协调各种蓝牙功能的运行。 2. 重要性 bt_main_thread 的重要性…...
城市应急安防系统EasyCVR视频融合平台:如何实现多源视频资源高效汇聚与应急指挥协同
一、方案背景 1)项目背景 在当今数字化时代,随着信息技术的飞速发展,视频监控和应急指挥系统在公共安全、城市应急等领域的重要性日益凸显。尤其是在关键场所,高效的视频资源整合与传输能力对于应对突发公共事件、实现快速精准的…...
自动化测试常用函数
自动化测试常用函数 前言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教程(七)---摄像机
本章参考官方教程:摄像机 本系列历史文 OpengGL教程(一)—OpenGL环境的配置(GLFW3,GLAD) OpengGL教程(二)—渲染一个简单的窗体 OpengGL教程(三)—使用VAO和VBO方式绘制三角形 OpengGL教程(四)—使用EBO方式绘制矩形 OpengGL教程(五)—纹理的应用 OpengGL教程(六)—…...
springboot禁用静态资源
使用 application.properties 在 application.properties 中添加如下配置: spring.web.resources.add-mappingsfalse使用 application.yml 在 application.yml 中添加如下配置: spring:web:resources:add-mappings: false配置解释 spring.web.resou…...
猫咪如厕检测与分类识别系统系列【二】多图上传及猫咪分类特征提取更新
前情提要 家里养了三只猫咪,其中一只布偶猫经常出入厕所。但因为平时忙于学业,没法时刻关注牠的行为。我知道猫咪的如厕频率和时长与健康状况密切相关,频繁如厕可能是泌尿问题,停留过久也可能是便秘或不适。为了更科学地了解牠的…...
ViewModel和AndroidViewModel的主要区别
ViewModel和AndroidViewModel的主要区别在于它们的继承关系、构造函数以及使用场景。 继承关系与构造函数: ViewModel:ViewModel是一个抽象类,位于androidx.lifecycle包中。它是所有ViewModel类的基类,构造函数较为…...
myeclise导入项目并运行
1、把项目复制到myeclise工作目录 2、导入项目:将项目导入到myeclise工作目录 3、配置jre 4、把项目放到tomcat 5、运行项目 配置数据库后运行...
XSS 防御转义规则笔记
一、转义规则概述 核心目标:防止用户输入被浏览器解析为可执行代码,确保输入始终被视为数据而非代码。 关键策略:根据数据嵌入的上下文环境(HTML、JavaScript、CSS 等),对特殊字符进行转义或编码。 二、不…...
【2025年认证杯数学中国数学建模网络挑战赛】C题 完整论文 全三问模型+求解+代码
目录 【2025年认证杯数学建模挑战赛】C题数据预处理与全三问求解一、问题重述二、模型假设与符号说明2.1 模型基本假设2.2 符号说明 三、数据预处理及分析四、问题一五、问题二5.1 基于互相关函数的反应延时识别5.2 反应过程延时结果分析5.3 基于BP神经网络的不合格产物预测5.4…...
iOS应用开发指南
开发一款iOS应用是一个系统化的过程,涵盖从环境搭建、界面设计、编码实现到测试发布的各个环节。以下是一份面向初学者的iOS移动应用开发指南,帮助你从零开始构建自己的App。 一、准备工作:开发环境与工具 必备设备 Mac电脑:iO…...
小刚说C语言刷题——第21讲 一维数组
在日常生活中,我们经常输入一组数据。例如输入一个班30名学生的语文成绩,或者输入一组商品的价格。这个时候,我们如何输入一组类型相同的数据呢?这里我们就要用到数组。 1.数组的概念 所谓数组就是一组相同类型数据的集合。数组中…...
苍穹外卖2
根据id查询员工 调用顺序:Controller—>Service—>ServiceImpl—>Mapper—>xml 1.controller层一个tab秒了(ai生成) 由于result泛型中希望返回一个employee对象,所以定义一个employee来接受Service的getbyid方法,在…...
C语言之双层for循环
一、第一小题引入 循环次数用 外层循环内层循环 这一题即是: 3412(次) 外循环一次 内循环一趟(可以形象理解为 时针和 分针) 运行结果如下: 一、第二小题引入 请阅读下列代码: 执行程序.程序执行的循环次数为多少? 答案:20次 这一题 外层循环 从5开始到i结束 中间的i是…...
第8课:多智能体系统评估与迭代
多智能体系统评估与迭代:从指标设计到持续优化的全流程指南 一、引言:当智能体协作出现“磨合期”:评估与迭代为何是必经之路? 在多智能体系统(MAS)的实际运行中,即使架构设计合理,也可能面临“协作效率下降”“资源浪费”等问题: 任务完成率突然从95%降至70%,却找…...
HTTP:三.HTTP报文
报文流 http报文是以一种类似的流的方式来发送数据的,所以报文流讲述了http报文的一些客观状态,相关术语:流入、流出形容事务处理。http报文任何时候是从上游向下游流入的!其中进过的节点既可能是上游,有可能是下游,如果从某个节点流出,那么相对于此节点流入的那个节点…...
.NET MAUI教程1-入门并发布apk包安装到真机
由于本人水平有限,如有写得不对的地方往指出,由于是使用公司的电脑,电脑的操作系统是英文的,没有权限修改,所以本文截图中的vs是英文版的 以发布android为例进行讲解 测试环境: window 11 vs2022 步骤如…...
你所拨打的电话是空号?手机状态查询API
一、引言 在当今数字化营销时代,电话销售仍然是许多企业获取客户的重要手段之一。然而,电销过程中常常遇到空号、风险号和沉默号等问题,这不仅降低了营销效率,还增加了企业的运营成本。例如,频繁拨打空号浪费了大量时…...
C++顺序栈的实现
顺序栈详细介绍 定义与特点 顺序栈(Sequential Stack)是一种基于数组实现的栈结构,利用数组的连续内存空间存储元素,遵循后进先出(LIFO)原则。其核心特点包括: 固定或动态容量:初始…...
element-ui 中的 select 组件如何 remote-method 函数中传参
在 select 组件中我们使用其 change 事件可以传参,请查看:el-select 中change 事件传参问题。 在我们使用 select 组件的远程搜索时,我们如何给 remote-method 这个方法添加自定义参数呢? 代码实现如下: <el-sel…...
浅谈解释型语言的运用
不得不说,程序不需要编译,程序在运行时才翻译成机器语言,每执行一次,都要翻译一次,因此效率比较低。在运行程序时才翻译,专门有一个解释器去进行翻译,每个语句都是执行的时候才翻译,…...
云手机哪个平台最好用?云手机性能、服务、技术等多维度测评分析
在云手机市场日渐繁荣的当下,面对琳琅满目的云手机平台,用户往往难以抉择云手机哪个平台最好。下面我们就从从性能、价格等多维度分析,云手机平台哪家强,队国内好用的云手机平台进行排行盘点。 好用的云手机平台排行盘点 第一&…...
idea版的cursor:Windsurf Wave 7
在企业环境中,Visual Studio Code和JetBrains系列是最常用的开发工具,覆盖了全球绝大多数开发者。这两类IDE各有优势,但JetBrains系列凭借其针对特定语言和企业场景的深度优化,使得用户很难轻易更换工具。 虽然Windsurf编辑器是基…...
【IDEA】创建 SpringBoot 项目连接 MySQL
前言 IntelliJ IDEA 作为一款专业的 Java 开发工具,在创建和管理 Spring Boot 项目方面具有显著优势。它深度集成了 Spring Boot 的开发流程,从项目初始化到最终部署都提供了高效的支持。IDEA 内置的 Spring Initializr 工具让项目创建变得极其简单&…...
使用 IntelliJ IDEA 进行远程调试
1. 前言 今天线上出现了个 Bug ,而且比较坑的是涉及到k8s环境相关的东西不能线下调试。传统方式是在代码中各种的日志 log 埋点然后重新部署进行调试,再根据 log 中的信息进行分析。如果你的 log 埋点不合理,就要不停的修改代码、不停的打包…...
Linux操作系统--进程状态
目录 1.运行、阻塞、挂起 1.1运行 1.2阻塞 1.3挂起(了解一下即可,基本不会出现这种情况) 2.进程状态 2.1进程状态查看 2.2 Z-僵尸进程 2.3孤儿进程 1.运行、阻塞、挂起 1.1运行 运行状态: 进程正在占用CPU执行指令。此时进程对系统资源ÿ…...
【端到端】端到端自动驾驶依赖Occupancy进行运动规划?还是可以具有生成局部地图来规划?
端到端自动驾驶系统的架构设计,目前主流做法实际上已经出现两种路径,我们来拆解一下: 🚘 一、Occupancy 是否用于运动规划? 一种趋势是使用 Occupancy 表示作为 中间表征,用于: 运动规划&…...
HarmonyOS-ArkUIV2装饰器-@Param:组件外部输入
上文我们了解了@Local装饰器 ,讲明了Local装饰器不允许外部传入值对其进行初始化。详见: HarmonyOS-ArkUI V2装饰器@Local装饰器:组件内部状态-CSDN博客。 但总有场景是需要外部组件传值过来,然后本组件接收这个值这种场景的。而且很多情况下,一个状态变量的作用范围会是…...
报错 ImportError: cannot import name ‘packaging‘ from ‘pkg_resources‘
解决方法一 (推荐) 先检查setuptools是否为70.0.0版本 pip list | grep setuptools再降低版本 python -m pip install setuptools69.5.1或者: 解决方法二 上述的报错信息表明:在安装 mmcv 时出现了 pkg_resources 模块的问题…...
Ollama调用多GPU实现负载均衡
文章目录 📊 背景说明🛠️ 修改 systemd 服务配置1. 配置文件路径2. 编辑服务文件2. 重新加载配置并重启服务3. 验证配置是否成功 📈 应用效果示例1. 调用单个70b模型2. 调用多个模型(70b和32b模型) 总结📌…...
Next.js 简介
Next.js 是一个由 Vercel 开发的基于 React 的 Web 开发框架,旨在简化 React 应用的开发流程,提供更好的性能和开发体验。 🌟 Next.js 的核心特点 1. 文件系统路由(File-system Routing) 在 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导出功能时,遇到了如下问题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,…...
iOS 上的内存管理是如何处理的?
iOS主要通过自动引用计数(ARC)和内存管理模型来处理内存。以下是对这两者的详细介绍以及在实际工作中的应用场景: 1. 自动引用计数(ARC) ARC是iOS和macOS中的内存管理机制。它能自动跟踪和管理应用程序的内存使用情况…...
河北工程大学e2e平台,python
题目,选择题包100分! 题目,选择题包100分! 题目,选择题包100分! 联系🛰:18039589633...
【Qt】spdlog日志模块的使用
版本 spdlog版本:1.5.0 采用1.5.0版本主要基于以下考虑:兼容Qt5.9.X版本和兼容C11。 spdlog 1.5.0下载地址:https://github.com/gabime/spdlog/releases/tag/v1.5.0 摘要 在Qt应用程序开发中,良好的日志系统至关重要。本文将介…...
python相关面试题
python相关面试题 1.上下文管理器需要实现哪两种方法以及相关应用 2.对比一下进程,线程和协程 3.魔法函数有哪些 4.什么是闭包和装饰器,装饰器缺点是什么 5.什么是浅拷贝和深拷贝 6.什么是GIL锁和互斥锁 7.init和new有什么区别,new方法的返回…...
swift菜鸟教程11-12(数组与字典)
一个朴实无华的目录 今日学习内容:1.Swift 数组1.1创建数组1.2访问数组1.3修改数组使用 append() 方法或者赋值运算符 在数组末尾添加元素通过索引修改数组元素的值: 1.4遍历数组 使用for-in循环同时需要每个数据项的值和索引值 1.5合并数组1.6count 属…...
.NET WPF 控件类分层结构
.NET WPF 控件类分层结构 在 WPF 中,类的层级结构设计是为了实现线程安全、依赖属性、可视化渲染、布局和交互等功能。以下是 WPF 核心基类的逐级说明,从最底层到最顶层: 1 DispatcherObject 作用: 提供与 WPF 线程模型…...
基于ImGui+FFmpeg实现播放器
基于ImGuiFFmpeg实现播放器 演示: ImGui播放器 继续研究FFmpeg,之前做了一个SDL的播放器,发现SDL的可视化UI界面的功能稍微差了点,所以今天我们换了一个新的工具,也就是ImGui。 ImGui官方文档:https://g…...