【C++】深入理解引用:从基础到进阶详解

news2024/9/20 14:33:09

🦄个人主页:小米里的大麦-CSDN博客

🎏所属专栏:C++_小米里的大麦的博客-CSDN博客

🎁代码托管:C++: 探索C++编程精髓,打造高效代码仓库 (gitee.com)

⚙️操作环境:Visual Studio 2022

目录

一、前言

二、引用的概念

三、常引用(const引用)

1. 权限只能缩小,不能放大

2. 具有常属性的临时参数

3. const引用的强接受度

4. 使用const引用传参,确保函数不修改参数

5. 小结

四、使用场景

1. 引用做函数参数

2. 引用做函数返回值

3. 小结

五、传值、传引用效率比较

六、引用和指针的区别

七、关于引用的主要注意事项归纳

1. 不能返回局部变量的引用

2. 引用不能引用空值

3. 引用的绑定一旦建立,就不能更改

4. 需要小心引用临时对象

总结 

共勉


一、前言

C++中的引用(reference)是一个非常重要的概念,它可以让你创建一个变量的别名,直接操作原始变量而不需要复制。引用在函数参数传递、返回值和效率优化等方面有广泛的应用。下面我们会一步步讲解引用的各个知识点,并搭配上由易到难的代码示例来帮助深入理解。

二、引用的概念

  • 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
  • 所以,引用本质上只是一个已有变量的别名,它必须在声明时被初始化,且不能更改为其他变量的引用。引用通过直接操作被引用的对象,实现与指针类似的效果,但语法上更简洁,不涉及指针的复杂运算。
  • 举几个例子,比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"鲁迅也是周树人;你是齐天大圣,是美猴王,是孙悟空,定不是吗喽。哈哈哈~

特点:

  • 引用一旦绑定到变量,不能改变绑定对象。
  • 引用不能为 null,必须指向有效的变量。
  • 引用必须在声明时初始化。
  • C++中不存在所谓的“二级引用”,因为引用总是直接绑定到一个具体的对象上,而不是其他引用。但是,通过多层次的引用绑定,您可以实现类似的效果。不过,这种方式通常不如直接使用多层指针来得直观和灵活。

int a = 10;
int& ref = a;  // ref 是 a 的引用
ref = 20;      // 相当于修改 a 的值
std::cout << a; // 输出 20

void swap(int& x, int& y) {
    int temp = x;
    x = y;
    y = temp;
}

int main() {
    int a = 5, b = 10;
    swap(a, b);  // 使用引用实现交换
    std::cout << "a: " << a << ", b: " << b; // 输出 a: 10, b: 5
}
在这个例子中,swap函数使用引用来交换两个整数的值,避免了传值复制带来的开销。

三、常引用(const引用)

常引用是引导绑定到一个常量或者一个不可修改的值。这在提交大型对象时尤其有用,因为可以避免不必要的拷贝,同时保证该对象不被修改。

int a = 10;
const int& ref = a;  // ref 是 a 的常引用
// ref = 20;        // 这样会报错,因为 ref 是常量引用
void TestConstRef()
{
	const int a = 10;
	//int& ra = a;   // 该语句编译时会出错,a为常量
	const int& ra = a;
	// int& b = 10; // 该语句编译时会出错,b为常量
	const int& b = 10;
	double d = 12.34;
	//int& rd = d; // 该语句编译时会出错,类型不同
	const int& rd = d;
}
void print(const std::string& str) {
    std::cout << str << std::endl;
}

int main() {
    std::string message = "Hello, World!";
    print(message);  // 通过常引用传递字符串,避免拷贝
}
这里,print函数使用常引用来避免拷贝std::string对象,提高性能,同时确保message不被修改。

1. 权限只能缩小,不能放大

在C++中,引用和指针的权限只能缩小,不能放大。即,const指针的引用或指针不能指向非const的指针。话虽这么说,试图把一个非const的引用/指针赋给一个const对象是安全的,但反之则不行。

int a = 10;
const int& ref = a;  // 正确:权限缩小,从非 const 到 const

这里,ref是一个const引用,绑定到一个非const变量a,这是允许的,因为ref的权限比较a小。
错误示例:权限放大(错误)

const int a = 10;
int& ref = a;  // 错误!不能将 const 变量绑定到非 const 引用

这里a是一个const变量,ref而不是一个const引用。尝试将a绑定到ref是不允许的,因为这样会放大权限。

2. 具有常属性的临时参数

临时变量(如字面常量、函数返回的非引用值等)会在表达式结束时思考。为了防止引用临时变量带来的问题,C++ 允许绑定到临时变量的引用必须是const引用,这样可以确保临时变量变量在生命周期内不被修改。

const int& ref = 10;  // 正确:临时变量只能绑定到 const 引用

在这个修改示例中,10是一个临时变量,编译器允许我们将它绑定到const引用ref,
以确保它在引用期间不会被引用。
错误示例:绑定到非const引用

int& ref = 10;  // 错误!不能将临时变量绑定到非 const 引用

这里,10是临时变量,非const引用不能绑定临时对象,因为临时对象用表达式结束时会联想。

3. const引用的强接受度

const引用可以绑定到多种类型的对象,包括:

  • const执行董事
  • const对象
  • 临时对象(如上所述)
  • 字面量

这使得const引用具有非常强的接受度,特别是在提交参数时,可以避免复制,提高效率。

例子:const引用绑定各种对象

int a = 5;
const int& ref1 = a;  // 绑定到非 const 对象
const int& ref2 = 10; // 绑定到字面量

在这个例子中,ref1绑定到非const对象a,ref2绑定到字面量10,两者都是合法的。
更复杂的例子:const引用绑定临时对象

std::string getMessage() {
    return "Hello, World!";
}

int main() {
    const std::string& msg = getMessage();  // 绑定临时字符串
    std::cout << msg;  // 输出 Hello, World!
}

这里getMessage()返回,一个临时对象,该临时对象被安全地绑定到const引用msg上。
由于msg是const引用,C++会确保临时对象在引用期间不会被回忆。

4. 使用const引用传参,确保函数不修改参数

在传递参数时,如果函数不打算修改参数,最好使用const引用。这样可以避免不必要的拷贝,尤其是对于大型对象(如std::stringstd::vector等),可以显着提高效率。

例子:传递const引用

void printMessage(const std::string& message) {
    std::cout << message << std::endl;
}

int main() {
    std::string msg = "Hello!";
    printMessage(msg);  // 通过 const 引用传参,避免拷贝
}

在这个例子中,printMessage函数通过const引用接收std::string,
保证不会修改声明的msg,同时避免了std::string的拷贝操作。
稍复杂的例子:传递大型对象的const引用

#include <vector>

void processVector(const std::vector<int>& vec) {
    for (int i : vec) {
        std::cout << i << " ";
    }
}

int main() {
    std::vector<int> nums = {1, 2, 3, 4, 5};
    processVector(nums);  // 通过 const 引用传递 vector,避免拷贝
}

在这个例子中,processVector通过const引用接收std::vector,
保证不修改原始数据,并且避免了传值时的拷贝,提高了效率。

5. 小结

  • 权限只能缩小,不能放大const引用可以绑定到非const对象,但返回不了。
  • 临时变量具有常量属性:临时对象只能绑定到const引用,以防止未定义行为。
  • const引用的接受度较高const引用可以绑定到非const对象、const对象、临时对象和字面量,应用场景广泛。
  • 使用const修改修改传参:当函数不需要确定的参数时,使用const引用可以避免不必要的拷贝,同时保证参数不被引用。

这些概念和注意事项有助于更好地理解和使用 C++ 的引用和const引用。

四、使用场景

1. 引用做函数参数

在C++修改中,使用引用作为函数参数可以传递大型对象时的开销,特别是当对象需要在函数内部修改时,传递引用能直接原始对象。

void addOne(int& num) {
    num += 1;
}

int main() {
    int a = 5;
    addOne(a);  // 通过引用修改 a
    std::cout << a;  // 输出 6
}
因为形参是实参的临时拷贝,所以要修改实参,以前需要传指针/地址才能做到
现在C++提供的引用就不需要那么麻烦了
但是,要注意,引用不是万能的,只能是:能不用指针就不用(指针有空指针、野指针,疏忽时会很麻烦),
同样,引用也有他的弊端(下文会讲到),但是没指针那么严重
void Swap(int& left, int& right)
{
   int temp = left;
   left = right;
   right = temp;
}
void scaleArray(int arr[], int size, int factor) {
    for (int i = 0; i < size; ++i) {
        arr[i] *= factor;
    }
}

int main() {
    int nums[] = {1, 2, 3, 4, 5};
    scaleArray(nums, 5, 2);  // 通过引用修改数组的元素
    for (int i : nums) {
        std::cout << i << " ";  // 输出 2 4 6 8 10
    }
}
这里,scaleArray函数直接操作数据库,因为数据库在C++中是默认以引用方式提交的。

2. 引用做函数返回值

返回函数引用时,可以返回原始对象的引用,而不是它的复制。这样做的一个好处是,可以继续对返回的对象进行操作。

int& getMax(int& x, int& y) {
    return (x > y) ? x : y;
}

int main() {
    int a = 10, b = 20;
    getMax(a, b) = 30;  // 修改较大的值
    std::cout << a << " " << b;  // 输出 10 30
}
int& Count()
{
    static int n = 0;
    n++;
    // ...
    return n;
}

int main() 
{
    std::cout << Count() << std::endl;
    //输出:1

    return  0;
}
int& getElement(int arr[], int index) {
    return arr[index];
}

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    getElement(arr, 2) = 10;  // 修改数组中的第三个元素
    for (int i : arr) {
        std::cout << i << " ";  // 输出 1 2 10 4 5
    }
}
在这个例子中,getElement函数返回数据库中元素的引用,这样可以直接修改数据库中的元素。

注意:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用

引用返回,如果已经还给系统了,则必须使用传值返回。

猜猜这个程序的输出结果是什么,为什么?

#include <iostream>
using namespace std;

int& Add(int a, int b)
{
	int c = a + b;
	return c;
}
int main()
{
	int& ret = Add(1, 2);
	Add(3, 4);
	cout << "Add(1, 2) is :" << ret << endl;
	return 0;
}
解释:
在 Add 函数中返回了一个局部变量 c 的引用。
由于 c 是在函数内部声明的一个局部变量,当函数执行完毕后,c 的生存期结束,它的内存会被释放。
这意味着在函数返回之后,任何对这个引用的使用都会导致未定义行为。

具体来说,在 main 函数中调用 Add(1, 2) 并将其结果赋值给 ret 时,ret 成为了局部变量 c 的引用。
但在 Add 函数返回之后,c 不再存在,因此 ret 成为一个无效的引用。
随后,当再次调用 Add(3, 4) 时,Add 函数会正常执行并返回,但此时 ret 仍然指向已被销毁的 c,
因此 cout 操作的结果是不确定的,可能导致程序崩溃或其他未定义行为。

如果确实需要返回引用,并且希望保持某些数据的一致性或状态,
可以考虑使用类成员函数来返回类内部的数据成员的引用。
在这种情况下,这些数据成员的生命周期至少与对象的生命周期一样长,因此是安全的。
但对于局部变量,它们的生命周期仅限于函数的作用域内,所以返回它们的引用是不安全的。

3. 小结

  • 基本任何场景都可以用引用传参
  • 谨慎用引用做返回值。出了函数作用域,对象不在了,就不能用引用返回u,还在就可以用引用返回

五、传值、传引用效率比较

传值时,函数会创建一个参数的副本,增加的对象可能导致性能大幅增加。而传值引用则不会复制对象,只是传递对象的别名,尤其是对大型对象和容器有利。

void byValue(std::string s) {
    // 拷贝 s
}

void byReference(const std::string& s) {
    // 通过引用传递,避免拷贝
}

int main() {
    std::string largeStr = "This is a very large string";
    byValue(largeStr);        // 性能较低,拷贝 largeStr
    byReference(largeStr);    // 性能较高,无拷贝
}
#include <vector>

void processVectorByValue(std::vector<int> vec) {
    vec.push_back(100);
}

void processVectorByReference(std::vector<int>& vec) {
    vec.push_back(100);
}

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    processVectorByValue(numbers);    // 传值,vec 是 numbers 的拷贝
    processVectorByReference(numbers); // 传引用,vec 是 numbers 的别名
    for (int i : numbers) {
        std::cout << i << " ";  // 输出 1 2 3 4 5 100
    }
}
这个例子显示了传值和传值的效率差异。通过引用引用时,不会创建副本,节省了时间和内存。

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。

#include <iostream>
#include <chrono>
#include <ctime>

struct A {
    int a[10000];
};

A g_a; // 全局变量

// 值返回
A TestFunc1() {
    return g_a;
}

// 引用返回
A& TestFunc2() {
    return g_a;
}

void TestReturnByRefOrValue() {
    // 以值作为函数的返回值类型
    auto start1 = std::chrono::high_resolution_clock::now();
    for (size_t i = 0; i < 100000; ++i) {
        TestFunc1(); // 使用函数的结果,否则编译器可能优化掉
        (void)i; // 防止未使用的警告
    }
    auto end1 = std::chrono::high_resolution_clock::now();

    // 以引用作为函数的返回值类型
    auto start2 = std::chrono::high_resolution_clock::now();
    for (size_t i = 0; i < 100000; ++i) {
        TestFunc2(); // 使用函数的结果,否则编译器可能优化掉
        (void)i; // 防止未使用的警告
    }
    auto end2 = std::chrono::high_resolution_clock::now();

    // 计算两个函数运算完成之后的时间
    auto duration1 = std::chrono::duration_cast<std::chrono::microseconds>(end1 - start1).count();
    auto duration2 = std::chrono::duration_cast<std::chrono::microseconds>(end2 - start2).count();

    std::cout << "TestFunc1 time: " << duration1 << " microseconds" << std::endl;
    std::cout << "TestFunc2 time: " << duration2 << " microseconds" << std::endl;
}

int main() {
    TestReturnByRefOrValue();
    return 0;
}
#include <iostream>
#include <chrono>

using namespace std;

struct A {
    int a[10000];
};

A g_a; // 全局变量

// 值返回
A TestFunc1() {
    return g_a;
}

// 引用返回
A& TestFunc2() {
    return g_a;
}

void TestReturnByRefOrValue() {
    // 以值作为函数的返回值类型
    auto start1 = std::chrono::high_resolution_clock::now();
    for (size_t i = 0; i < 100000; ++i) {
        auto result1 = TestFunc1(); // 使用函数的结果
        (void)result1; // 防止未使用的警告
    }
    auto end1 = std::chrono::high_resolution_clock::now();

    // 以引用作为函数的返回值类型
    auto start2 = std::chrono::high_resolution_clock::now();
    for (size_t i = 0; i < 100000; ++i) {
        auto& result2 = TestFunc2(); // 使用函数的结果
        (void)result2; // 防止未使用的警告
    }
    auto end2 = std::chrono::high_resolution_clock::now();

    // 计算两个函数运算完成之后的时间
    auto duration1 = std::chrono::duration_cast<std::chrono::microseconds>(end1 - start1).count();
    auto duration2 = std::chrono::duration_cast<std::chrono::microseconds>(end2 - start2).count();

    cout << "TestFunc1 time: " << duration1 << " microseconds" << endl;
    cout << "TestFunc2 time: " << duration2 << " microseconds" << endl;
}

int main() {
    TestReturnByRefOrValue();
    return 0;
}

通过上述代码的比较,发现传值和指针在作为传参以及返回值类型上效率相差很大

六、引用和指针的区别

  • 引用:必须在声明时初始化,无法改变引用的对象。
  • 指针:可以初始化为null,并且可以指向不同的对象。
  • 语法:引用使用&,指针使用*->
int a = 10;
int* p = &a;  // 指针 p 指向 a 的地址
int& ref = a; // ref 是 a 的引用

*p = 20;  // 修改指针指向的值
ref = 30; // 修改引用绑定的值
std::cout << a;  // 输出 30
void swapPointer(int* x, int* y) {
    int temp = *x;
    *x = *y;
    *y = temp;
}

void swapReference(int& x, int& y) {
    int temp = x;
    x = y;
    y = temp;
}

int main() {
    int a = 10, b = 20;
    swapPointer(&a, &b);  // 使用指针交换
    swapReference(a, b);  // 使用引用交换
    std::cout << a << " " << b;  // 输出 20 10
}
在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
int main()
{
	int a = 10;
	int& ra = a;
	cout << "&a = " << &a << endl;
	cout << "&ra = " << &ra << endl;
	return 0;
}
在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
int main()
{
	int a = 10;
	int& ra = a;
	ra = 20;
	int* pa = &a;
	*pa = 20;
	return 0;
}

指针更灵活,但需要手动解引用和处理空指针,而引用更安全、语法更简单。 

引用和指针的不同点一览:

  1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
  2. 引用在定义时必须初始化,指针没有要求
  3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  4. 没有NULL引用,但有NULL指针
  5. 在sizeof中含义不同引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  7. 有多级指针,但是没有多级引用
  8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  9. 引用比指针使用起来相对更安全

七、关于引用的主要注意事项归纳

1. 不能返回局部变量的引用

在函数中,局部变量的生命周期在函数结束后就结束了。如果返回一个局部变量的引用,程序行为会变得不可预测,该引用指向的内存可能已经被释放或被其他数据占用。

错误示例:

int& Count(int x) {
    int n = x;  // 局部变量 n
    n++;
    return n;   // 返回局部变量的引用,错误!
}
在这个例子中,n是局部变量,函数返回时它的生命周期就结束了,导致返回的引用指向无效的内存。


正确的做法:使用静态变量 静态变量的生命周期从函数第一次调用一直到程序结束,
因此可以安全地返回它的引用。
正确的示例:

int& Count(int x) {
    static int n = x;  // 静态变量,生命周期贯穿整个程序
    n++;
    return n;          // 返回静态变量的引用
}
在这个例子中,n是静态变量,虽然它是在函数内部定义的,但它的生命周期是整个程序运行期,
因此可以安全地返回它的引用。

2. 引用不能引用空值

绑定到一个合法的变量,不能指向null或未初始化的引用必须是内存。这是引用与指针的一个主要区别。

错误示例:

int* p = nullptr;
int& ref = *p;  // 错误!引用不能指向 null
示例中,p是一个空指针,尝试解引用它并创建引用是未行为定义。

3. 引用的绑定一旦建立,就不能更改

与指针不同,引用在初始化后不能被更改到其他对象。它只能永久绑定到第一个初始化时的对象。

int a = 10;
int b = 20;
int& ref = a;  // ref 引用 a
ref = b;       // 这不会改变 ref 的绑定对象,而是将 b 的值赋给 a
std::cout << a << " " << b;  // 输出 20 20

ref = b;并不会ref引用b,它实际上是把b的值赋予了a,因此a最后b都变成了20。

4. 需要小心引用临时对象

临时对象在表达结束后会引入,因此引用一个临时对象是非常危险的操作。

错误示例:

const int& ref = 5;  // 虽然可以编译,但要小心临时对象的生命周期

在这种情况下,编译器会优化,创建一个临时的常量变量,但对非const引用来说,这是不允许的。

总结 

  • 引用的主要作用是在函数传参和返回值中减少不必要的复制操作,提高程序的运行效率。
  • const引用是非常灵活和常用的,能够接收多种类型的对象,包括字面量和临时对象,广泛用于保证数据不被修改。
  • 注意生命周期和局部变量引用问题,避免程序指向无效内存。
  • 权限控制在C++引用中非常重要的保证,引用的权限只能缩小而不能放大,有助于保证数据的安全性。

通过以上的讲解,相信你对 C++ 引用的概念、特性和应用场景有了更深、更全面的理解。本文制作、整理、总结不易,对你有帮助的话,还请留下三连以表支持!感谢!!

共勉

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

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

相关文章

c++249多态

#include<iostream> using namespace std; class Parent { public:Parent(int a){this->a a;cout << " Parent" << a << endl;} public:virtual void print()//在子类里面可写可不写 {cout << "Parent" <<a<&l…

虚拟机centos_7 配置教程(镜像源、配置centos、静态ip地址、Finalshell远程操控使用)

文章目录 一、下载镜像源&#xff08;准备工作&#xff09;1、开源网站2、下载 二、VMware配置centos三、配置静态IP地址四、Finalshell使用1、下载Finalshell2、连接虚拟机 五、谢谢观看&#xff01; 一、下载镜像源&#xff08;准备工作&#xff09; 1、开源网站 有许多开源…

[Linux]:进程间通信(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;Linux学习 贝蒂的主页&#xff1a;Betty’s blog 1. system V通信 前面我们所探究的通信方式都是基于管道文件的&#xff0c;而…

day22JS-npm中的部分插件使用方法

1. 静态资源目录 静态资源目录就是访问服务器的某些路劲时候&#xff0c;服务器可以吐出一个写好的指定页面。 实现思路&#xff1a; 1、先判断要找的路径是否是文件&#xff0c;如果是文件&#xff0c;就加载发给对方。 2、如果是文件夹&#xff0c;找到这个文件夹所在路径中…

STL相关简介

string 看到这个词&#xff0c;相信大家一定都很好奇什么是string&#xff0c;它有什么作用呢&#xff1f;今天&#xff0c;就让我们一起来了解一下关于string的简介吧~ 目录 string 1. 什么是STL 2. STL的版本 3. STL的六大组件 4. STL的重要性 5. 如何学习STL 6.STL的…

modbus调试助手/mqtt调试工具/超轻巧物联网组件/多线程实时采集/各种协议支持

一、前言说明 搞物联网开发很多年&#xff0c;用的最多的当属modbus协议&#xff0c;一个稳定好用的物联网组件是物联网平台持续运行多年的基石&#xff0c;所以这个物联网组件从一开始就定位于自研&#xff0c;为了满足各种场景的需求&#xff0c;当然最重要的一点就是大大提…

【题解】—— [CSP-J2019 江西] 面积

【题解】—— [CSP-J2019 江西] 面积 [CSP-J2019 江西] 面积题目描述输入格式输出格式输入输出样例输入 #1输出 #1输入 #2输出 #2 提示 1.思路解析2.AC代码 [CSP-J2019 江西] 面积 通往洛谷的传送门 题目描述 Alice 有一个边长为 a a a 的正方形&#xff0c;Bob 有一个长宽…

【Linux】生产者消费者模型:基于阻塞队列,使用互斥锁和条件变量维护互斥与同步关系

目录 一、什么是生产者消费者模型 二、为什么要引入生产者消费者模型&#xff1f; 三、详解生产者消费者模型 ​编辑 生产者和生产者、消费者和消费者、生产者和消费者&#xff0c;它们之间为什么会存在互斥关系&#xff1f; 生产者和消费者之间为什么会存在同步关系&…

信奥学习规划(CSP-J/S)

CSP-J组学习路线规划 CSP-S组学习规划

AbMole带你解密穿心莲:天然植物中的抗癌新星

在浩瀚的自然界中&#xff0c;隐藏着无数未被完全发掘的宝藏&#xff0c;其中&#xff0c;穿心莲&#xff08;Andrographis paniculata&#xff09;作为一种传统的药用植物&#xff0c;正逐渐展现出其在现代医学研究中的独特魅力。近日&#xff0c;一项发表在《Phytomedicine》…

Jemter项目实战(黑马程序员)

视频网址&#xff1a;02测试数据准备_哔哩哔哩_bilibili 自动化脚本架构搭建 新增和修改 新增 删除和查询 弱压力、高并发、高频率 弱压力测试 高并发 高频率 生成图形化报告

深入理解命令模式:行为设计模式的精髓

在软件设计中&#xff0c;命令模式&#xff08;Command Pattern&#xff09;是一种行为设计模式&#xff0c;它将请求封装成对象&#xff0c;从而使你可以用不同的请求对客户进行参数化&#xff0c;并且支持请求的排队、记录日志以及撤销操作。命令模式是设计模式中的一种&…

通信工程学习:什么是EPON以太网无源光网络

EPON&#xff1a;以太网无源光网络 EPON&#xff08;Ethernet Passive Optical Network&#xff0c;以太网无源光网络&#xff09;是一种结合了以太网技术和无源光网络&#xff08;PON&#xff09;优势的高速、大容量宽带接入技术。以下是关于EPON的详细解释&#xff1a; 一、…

【经典文献】双边滤波

文章目录 ICCV 1998基本思路双边高斯滤波 ICCV 1998 1995年&#xff0c;Aurich和Weule提出一种非线性高斯滤波器&#xff0c;三年后&#xff0c;Tomasi和Manduchi将其用于图像平滑&#xff0c;并将其命名为双边滤波。 Aurich, V., & Weule, J. (1995). Non-linear Gaussi…

Git常用指令整理【新手入门级】【by慕羽】

Git 是一个分布式版本控制系统&#xff0c;主要用于跟踪和管理源代码的更改。它允许多名开发者协作&#xff0c;同时提供了强大的功能来管理项目的历史记录和不同版本。本文主要记录和整理&#xff0c;个人理解的Git相关的一些指令和用法 文章目录 一、git安装 & 创建git仓…

蓝桥杯-STM32G431RBT6(串口)

前言 一、配置 二、使用步骤 1.串口发送 代码逻辑 效果展示 2.串口接收单个字符 代码逻辑 中断回调函数 3.串口接受字符串 代码逻辑 字符串函数 中断回调函数 声明 代码开源 前言 一、配置 二、使用步骤 1.串口发送 代码逻辑 sprintf(tx_buf,"jin ke\r\n&…

(学习总结17)C++继承

C继承 一、继承的概念与定义继承的概念继承定义1. 定义格式2. 继承基类成员访问方式的变化 继承类模板 二、基类和派生类间的转换三、继承中的作用域隐藏规则 四、派生类的默认成员函数4个常见默认成员函数实现一个不能被继承的类 五、继承与友元六、继承与静态成员七、多继承及…

DFS:二叉树中的深搜

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一. 深搜的总结 二. 二叉树的深搜题目 2.1 计算布尔二叉树的值 2.2 求根节点到叶节点的数字之和 2.3 二叉树剪枝 2.4 验证二叉搜索树 2.5 二叉搜索树中第k小的节点 2.6 二叉树的…

【C++】——继承详解

目录 1、继承的概念与意义 2、继承的使用 2.1继承的定义及语法 2.2基类与派生类间的转换 2.3继承中的作用域 2.4派生类的默认成员函数 <1>构造函数 <2>拷贝构造函数 <3>赋值重载函数 <4析构函数 <5>总结 3、继承与友元 4、继承与静态变…

在VC++6.0中创建一个C++项目

1、下载并安装VC6.0 暂时不介绍下载安装&#xff0c;后续可能会补充 2、打开VC6.0 初始界面如下图&#xff1a; 3、创建一个空工程 文件-新建 在新建弹框中选择&#xff1a;工程-win32 console Application-填写工程名、选择保存路劲-确定 在新的弹框中&#xff0c;选择&…