C++基础(七):类和对象之深浅拷贝问题(中-3)

news2024/12/27 18:01:04

         在C++编程中,拷贝构造函数是对象复制的核心机制,尤其是在处理对象间的值传递时。当一个对象通过拷贝另一个对象来初始化时,浅拷贝只是简单地复制对象的成员变量的值。如果对象包含指针成员,浅拷贝只复制指针地址,这可能会导致两个对象共享同一块内存资源,容易引发内存泄漏或悬挂指针的问题。深拷贝则会在拷贝过程中为指针成员分配新的内存,并复制实际的数据,确保每个对象都有独立的资源,避免了上述问题。理解并正确实现这两种拷贝方式,不仅可以避免常见的资源管理问题,还能提升代码的健壮性和稳定性。接下来,我们将深入探讨C++中拷贝构造函数的深浅拷贝问题及实现MyString。

一、引入浅拷贝问题

       自己实现一个字符串的类,并且使用系统编译器默认生成的拷贝构造函数,代码如下:

#include<iostream>
#include<string.h>
using namespace std;

class MyString
{
private:
	char* ptr;

public:


	MyString(const char* str = nullptr)
	{
		if (str != nullptr)
		{
			int len = strlen(str);    //字符串实际长度
			ptr = new char[len + 1];  //申请堆区内存空间要比实际字符串长度多一个,因为要存储'\0'
			strcpy(ptr, str);
		}
		else
		{
			ptr = new char[1];       
			*ptr = '\0';
		}
	}

	~MyString()
	{
		if (ptr != nullptr)
		{
			delete[] ptr;		
		}
		ptr = nullptr;
	}

	void PrintString() const
	{
		if (ptr != nullptr)
		{
			cout << ptr << endl;
		}
	}


};


int main()
{
	MyString s1("wanghenghello");
	s1.PrintString();

	MyString s2(s1);
	s2.PrintString();
	return 0;
}

分析上述代码存在的问题?

      如果String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,或者用s1给s2赋值时,编译器会调用默认的拷贝构造或者赋值运算符重载。最终导致的问题是,s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

则会导致以下问题:

  • 双重释放:如果两个对象共享同一块内存,当其中一个对象被销毁时,它会释放这块内存。然后,另一个对象在销毁时也会尝试释放这块内存,导致程序崩溃或未定义行为。
  • 数据损坏:两个对象共享同一块内存时,对其中一个对象的修改可能会影响另一个对象的数据,导致数据不一致或错误。

如何解决这个问题呢?

      可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享,深拷贝:给每个对象独立分配资源,保证多个对象之间不会因共享资源而造成多次释放造成程序奔溃问题!

二、自己实现深拷贝

#include<iostream>
#include<string.h>
using namespace std;

class MyString
{
private:
	char* ptr;

public:


	MyString(const char* str = nullptr)
	{
		if (str != nullptr)
		{
			int len = strlen(str);    //字符串实际长度
			ptr = new char[len + 1];  //申请堆区内存空间要比实际字符串长度多一个,因为要存储'\0'
			strcpy(ptr, str);
		}
		else
		{
			ptr = new char[1];       
			*ptr = '\0';
		}
	}

	~MyString()
	{
		if (ptr != nullptr)   //必须要判空,空字符指针不可以求字符串长度!
		{
			delete[] ptr;		
		}
		ptr = nullptr;
	}

	void PrintString() const
	{
		if (ptr != nullptr)
		{
			cout << ptr << endl;
		}
	}


	//实现深拷贝的拷贝构造函数
	MyString(const MyString& it)
	{
		if (it.ptr != nullptr)    //必须要判空,空字符指针不可以求字符串长度!
		{
			int len = strlen(it.ptr);
			this->ptr = new char[len + 1];
			strcpy(this->ptr, it.ptr);
		}
		else
		{
			this->ptr = nullptr;
		}
	}



   //实现深拷贝的赋值运算符重载operator=() 
	/***********************************************************关于函数的返回值设计是传值返回还是传引用返回,该如何选取?*************************************************
	* 
	1、传值返回,会构建临时的将亡值对象,调用拷贝构造函数;
    2、传引用返回,不会构建临时的将亡值对象,直接返回对象本身;
	3、但是需要注意一点:传引用返回,必须是该对象的生命周期不会受到函数调用的影响,才可以以引用返回;
	4、总结:实际开发中,如果该对象对象的生命周期不会受到函数调用的影响(比如全局对象、静态对象、堆区数据的定义、主函数定义的对象),传值返回和传引用返回都可以,但是为了减少拷贝构造提高效率,通常采用传引用返回!

	**************************************************************************************************************************************/
	//s2=s1;
	//s2.operator=(s1);
	//operator=(&s2,s1);

	MyString& operator=(const MyString& it)
	{
		if (this != &it)                 //自我复制没有意义,提前判断。
		{
			if (this->ptr != nullptr)   //s2对象本身与一块内存绑定,为防止后续指针赋值,原来的内存出现泄露,提前判断,并且释放原内存,将指针置空。
			{
				delete [] this-> ptr;
				this->ptr = nullptr;
			}


			if (it.ptr != nullptr)
			{
				int len = strlen(it.ptr);
				ptr = new char[len + 1];
				strcpy(ptr, it.ptr);
		    }

		}
		return *this;     //支持连续赋值
	}

};


int main()
{
	MyString s1("wanghenghello");
	s1.PrintString();

	MyString s2(s1);
	s2.PrintString();


    MyString s3("111");
    MyString s4("222");
    s3.PrintString();
    s4.PrintString();

    s4=s3;
    s3.PrintString();
    s4.PrintString();

	return 0;
}

关于函数的返回值设计是传值返回还是传引用返回,该如何选取?

  1. 传值返回,会构建临时的将亡值对象,调用拷贝构造函数;
  2. 传引用返回,不会构建临时的将亡值对象,直接返回对象本身;
  3. 但是需要注意一点:传引用返回,必须是该对象的生命周期不会受到函数调用的影响,才可以以引用返回;

       总结:实际开发中,如果该对象对象的生命周期不会受到函数调用的影响(比如全局对象、静态对象、堆区数据的定义、主函数定义的对象),传值返回和传引用返回都可以,但是为了减少拷贝构造提高效率,通常采用传引用返回!赋值运算符重载这里必须以引用返回,return *this; 因为这样它可以支持连续赋值!

 核心代码如下:


	MyString& operator=(const MyString& it)
	{
		if (this != &it)                 //自我复制没有意义,提前判断。
		{
			if (this->ptr != nullptr)   //s2对象本身与一块内存绑定,为防止后续指针赋值,原来的内存出现泄露,提前判断,并且释放原内存,将指针置空。
			{
				delete [] this-> ptr;
				this->ptr = nullptr;
			}


			if (it.ptr != nullptr)
			{
				int len = strlen(it.ptr);
				ptr = new char[len + 1];
				strcpy(ptr, it.ptr);
		    }

		}
		return *this;     //支持连续赋值
	}

三、移动构造和移动赋值

3.1  引入

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;

class MyString
{
private:
	char* ptr;

public:


	MyString(const char* str = nullptr)
	{
		if (str != nullptr)
		{
			int len = strlen(str);    //字符串实际长度
			ptr = new char[len + 1];  //申请堆区内存空间要比实际字符串长度多一个,因为要存储'\0'
			strcpy(ptr, str);
		}
		else
		{
			ptr = new char[1];       
			*ptr = '\0';
		}
		cout << "Create MyString : " << this << endl;
	}


	~MyString()
	{
		if (ptr != nullptr)
		{
			delete[] ptr;		
		}
		ptr = nullptr;
		cout << "~MyString : " << this << endl;
	}

	void PrintString() const
	{
		if (ptr != nullptr)
		{
			printf("%p => %s \n", ptr, ptr);
		}
	}


	//实现深拷贝的拷贝构造函数
	MyString(const MyString& it)
	{
		if (it.ptr != nullptr)    
		{
			int len = strlen(it.ptr);
			this->ptr = new char[len + 1];
			strcpy(this->ptr, it.ptr);
		}
		else
		{
			this->ptr = nullptr;
		}
		cout <<this<< "Copy MyString : " << &it << endl;
	}


	MyString& operator=(const MyString& it)
	{
		if (this != &it)                
		{
			if (this->ptr != nullptr)   
			{
				delete [] this-> ptr;
				this->ptr = nullptr;
			}


			if (it.ptr != nullptr)
			{
				int len = strlen(it.ptr);
				ptr = new char[len + 1];
				strcpy(ptr, it.ptr);
		    }

		}
		cout << this <<  "operator=() " << &it << endl;
		return *this;             //支持连续赋值
	}

};



MyString func(const char*p)
{
	MyString tmp(p);
	tmp.PrintString();
	return tmp;
}


int main()
{
	MyString s1("newdata");
	s1.PrintString();

	s1 = func("hello");  //将亡值对象为s1对象赋值
	s1.PrintString();
	return 0;
}

存在问题:在堆区反复构建对象,尤其是构建将亡值对象和深赋值运算符重载函数的时候都需要构建新的对象!如何减少呢?先学习下面的内容,最后解决。

3.2 移动构造

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;

class MyString
{
private:
	char* ptr;

public:


	MyString(const char* str = nullptr)
	{
		if (str != nullptr)
		{
			int len = strlen(str);    //字符串实际长度
			ptr = new char[len + 1];  //申请堆区内存空间要比实际字符串长度多一个,因为要存储'\0'
			strcpy(ptr, str);
		}
		else
		{
			ptr = new char[1];       
			*ptr = '\0';
		}
		cout << "Create MyString : " << this << endl;
	}


	~MyString()
	{
		if (ptr != nullptr)
		{
			delete[] ptr;		
		}
		ptr = nullptr;
		cout << "~MyString : " << this << endl;
	}

	void PrintString() const
	{
		if (ptr != nullptr)
		{
			printf("%p => %s \n", ptr, ptr);
		}
	}


	//实现深拷贝的拷贝构造函数
	MyString(const MyString& it)
	{
		if (it.ptr != nullptr)    
		{
			int len = strlen(it.ptr);
			this->ptr = new char[len + 1];
			strcpy(this->ptr, it.ptr);
		}
		else
		{
			this->ptr = nullptr;
		}
		cout <<this<< "Copy MyString : " << &it << endl;
	}


	//实现移动构造函数
	MyString(MyString&& it):ptr(it.ptr)
	{
		it.ptr = nullptr;
		cout << this << "Move  Copy MyString : " << &it << endl;
	}


	MyString& operator=(const MyString& it)
	{
		if (this != &it)                
		{
			if (this->ptr != nullptr)   
			{
				delete [] this-> ptr;
				this->ptr = nullptr;
			}


			if (it.ptr != nullptr)
			{
				int len = strlen(it.ptr);
				ptr = new char[len + 1];
				strcpy(ptr, it.ptr);
		    }

		}
		cout << this <<  "operator=() " << &it << endl;
		return *this;             //支持连续赋值
	}

};


int main()
{
	MyString s1("newdata");
	MyString s2(std::move(s1));   //C++11提供的移动拷贝构造函数

	s1.PrintString();
	s2.PrintString();
	return 0;
}

 核心代码如下:

	//实现移动构造函数
	MyString(MyString&& it):ptr(it.ptr)
	{
		it.ptr = nullptr;
		cout << this << "Move  Copy MyString : " << &it << endl;
	}

3.3 移动赋值运算符重载

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;

class MyString
{
private:
	char* ptr;

public:


	MyString(const char* str = nullptr)
	{
		if (str != nullptr)
		{
			int len = strlen(str);    //字符串实际长度
			ptr = new char[len + 1];  //申请堆区内存空间要比实际字符串长度多一个,因为要存储'\0'
			strcpy(ptr, str);
		}
		else
		{
			ptr = new char[1];       
			*ptr = '\0';
		}
		cout << "Create MyString : " << this << endl;
	}


	~MyString()
	{
		if (ptr != nullptr)
		{
			delete[] ptr;		
		}
		ptr = nullptr;
		cout << "~MyString : " << this << endl;
	}

	void PrintString() const
	{
		if (ptr != nullptr)
		{
			printf("%p => %s \n", ptr, ptr);
		}
	}


	//实现深拷贝的拷贝构造函数
	MyString(const MyString& it)
	{
		if (it.ptr != nullptr)    
		{
			int len = strlen(it.ptr);
			this->ptr = new char[len + 1];
			strcpy(this->ptr, it.ptr);
		}
		else
		{
			this->ptr = nullptr;
		}
		cout <<this<< "Copy MyString : " << &it << endl;
	}


	//实现移动构造函数
	MyString(MyString&& it):ptr(it.ptr)
	{
		it.ptr = nullptr;
		cout << this << "Move  Copy MyString : " << &it << endl;
	}

	//实现的赋值运算符重载函数
	MyString& operator=(const MyString& it)
	{
		if (this != &it)                
		{
			if (this->ptr != nullptr)   
			{
				delete [] this-> ptr;
				this->ptr = nullptr;
			}


			if (it.ptr != nullptr)
			{
				int len = strlen(it.ptr);
				ptr = new char[len + 1];
				strcpy(ptr, it.ptr);
		    }

		}
		cout << this <<  "operator=() " << &it << endl;
		return *this;             //支持连续赋值
	}



	//实现的移动赋值运算符重载函数
	MyString& operator=( MyString &&it)
	{
		if (this != &it)
		{
			if (this->ptr != nullptr)
			{
				delete[] this->ptr;
				this->ptr = nullptr;
			}


			if (it.ptr != nullptr)
			{
				this->ptr = it.ptr;
				it.ptr = nullptr;
			}

		}
		cout << this << " operator= move " << &it << endl;
		return *this;             //支持连续赋值
	}

};


int main()
{
	MyString s1("newdata");
	MyString s2("hello");   
	s1.PrintString();
	s2.PrintString();

	s2 = std::move(s1);  //C++11提供的移动赋值运算符重载函数
	s1.PrintString();
	s2.PrintString();
	return 0;
}
       移动赋值和移动构造函数实现的原理一样,将指针的指向进行赋值,再将原来的对象的指针置为空!它的实现条件也是两个,首先他要是一个右值对象(通过move函数实现),然后,我们需要实现移动赋值运算符重载函数!

核心代码如下:

//实现的移动赋值运算符重载函数
	MyString& operator=( MyString &&it)
	{
		if (this != &it)
		{
			if (this->ptr != nullptr)
			{
				delete[] this->ptr;
				this->ptr = nullptr;
			}


			if (it.ptr != nullptr)
			{
				this->ptr = it.ptr;
				it.ptr = nullptr;
			}

		}
		cout << this << " operator= move " << &it << endl;
		return *this;             //支持连续赋值
	}

3.4 解决问题

        到现在,我们来解决前面留下的问题,加入移动构造和移动赋值即可!

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;

class MyString
{
private:
	char* ptr;

public:


	MyString(const char* str = nullptr)
	{
		if (str != nullptr)
		{
			int len = strlen(str);    //字符串实际长度
			ptr = new char[len + 1];  //申请堆区内存空间要比实际字符串长度多一个,因为要存储'\0'
			strcpy(ptr, str);
		}
		else
		{
			ptr = new char[1];       
			*ptr = '\0';
		}
		cout << " Create MyString : " << this << endl;
	}


	~MyString()
	{
		if (ptr != nullptr)
		{
			delete[] ptr;		
		}
		ptr = nullptr;
		cout << " ~MyString : " << this << endl;
	}

	void PrintString() const
	{
		if (ptr != nullptr)
		{
			printf("%p => %s \n", ptr, ptr);
		}
	}


	//实现深拷贝的拷贝构造函数
	MyString(const MyString& it)
	{
		if (it.ptr != nullptr)    
		{
			int len = strlen(it.ptr);
			this->ptr = new char[len + 1];
			strcpy(this->ptr, it.ptr);
		}
		else
		{
			this->ptr = nullptr;
		}
		cout << this<< " Copy MyString : " << &it << endl;
	}


	//实现移动构造函数
	MyString(MyString&& it):ptr(it.ptr)
	{
		it.ptr = nullptr;
		cout << this << " Move  Copy MyString : " << &it << endl;
	}

	//实现的赋值运算符重载函数
	MyString& operator=(const MyString& it)
	{
		if (this != &it)                
		{
			if (this->ptr != nullptr)   
			{
				delete [] this-> ptr;
				this->ptr = nullptr;
			}


			if (it.ptr != nullptr)
			{
				int len = strlen(it.ptr);
				ptr = new char[len + 1];
				strcpy(ptr, it.ptr);
		    }

		}
		cout << this <<  "operator=() " << &it << endl;
		return *this;             //支持连续赋值
	}



	//实现的移动赋值运算符重载函数
	MyString& operator=( MyString &&it)
	{
		if (this != &it)
		{
			if (this->ptr != nullptr)
			{
				delete[] this->ptr;
				this->ptr = nullptr;
			}


			if (it.ptr != nullptr)
			{
				this->ptr = it.ptr;
				it.ptr = nullptr;
			}

		}
		cout << this << " operator= move " << &it << endl;
		return *this;             //支持连续赋值
	}

};




MyString func(const char*p)
{
	MyString tmp(p);
	tmp.PrintString();
	return tmp;
}


int main()
{
	MyString s1("newdata");
	s1.PrintString();

	s1 = func("hello");
	s1.PrintString();
	return 0;
}

分析:

        可以看到,加入移动构造函数和移动赋值运算符重载函数后,用tmp构建不具名的将亡值对象时,调用的是移动构造函数,然后返回给主函数,在进行赋值的时候,由于不具名的将亡值对象是右值对象,因此,它也调用的是移动赋值运算符重载函数!!!这样减少了在堆区重新开辟内存空间的次数!提高了效率!

至此,C++面向对象-中全部内容就学习完毕,这一节内容比较重要,建议多看几遍,认真复习消化,熟练使用,C++相对来说较为复杂,我们应该时刻理清自己的思路,耐下心来,一点点积累, 星光不问赶路人,加油吧,感谢阅读,如果对此专栏感兴趣,点赞加关注! 

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

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

相关文章

基于matlab交通标志识别系统用的APP designer设计的gui界面 交互原理:bp神经网络-训练好图像处理有灰度化-二值化-颜色区域定位识别

基于MATLAB的交通标志识别系统是一个实用的工具&#xff0c;用于识别道路交通标志。该系统结合了图像处理技术和BP神经网络模型&#xff0c;可以在给定的图像中定位并识别交通标志。通过使用MATLAB的App Designer工具&#xff0c;系统还提供了一个交互式的图形用户界面&#xf…

【idea-安装】

JetBrains官⽹ : https://www.jetbrains.com/ 1.下载idea安装包&#xff0c;下载旧一些的版本&#xff0c;避免新版本的不稳定。 下载下来的安装包是exe格式的&#xff0c;直接点击运行。 点击Next 2.选择要下载的位置&#xff0c;点击下一步。 3.选择⽣成快捷⽅式和建⽴⽂件…

从头开始学MyBatis—01搭建基础框架

首先对mybatis进行一个简单的介绍&#xff0c;然后从最基础的准备工作开始搭建一个mybatis的开发 环境&#xff0c;最后通过简单的增删改查来熟悉配置文件和映射文件的编写。 目录 1.Mybaits简介 2.开发环境和准备工作 2.1开发环境 2.2pom文件 2.3数据库和表 2.4对应实体…

探索Python世界的隐藏宝石:Pika库的神秘力量

文章目录 探索Python世界的隐藏宝石&#xff1a;Pika库的神秘力量背景&#xff1a;为何选择Pika&#xff1f;Pik库简介如何安装Pika&#xff1f;简单库函数使用方法场景应用常见Bug及解决方案总结 探索Python世界的隐藏宝石&#xff1a;Pika库的神秘力量 背景&#xff1a;为何…

【机器学习】8 ——朴素贝叶斯

机器学习 8 ——朴素贝叶斯 特征条件独立假设 朴素是指每个特征独立地影响结果&#xff0c;整个假设在实际应用中不成立&#xff0c;主要是思想 输入输出的来拟合概率分布&#xff0c;贝叶斯定理&#xff0c;后验概率最大 文章目录 机器学习 8 ——朴素贝叶斯前言贝叶斯定理先…

vue2使用ag-grid表格

ag-grid官网&#xff1a;Vue Grid: Custom Components | AG Grid 根据官方文档说的AG Grid no longer supports Vue 2. The last version to support Vue 2 is AG Grid v31.3.&#xff0c;目前只有v31.3.版本支持vue2。 以下是官方给的demo Vue Grid: Get Started with AG G…

C# WPF编程-串口通信

C# WPF编程-串口通信 串口通信1. NuGet安装System.IO.Ports2. 界面布局XAML3. C#代码4. 运行效果源码下载 串口通信 1. NuGet安装System.IO.Ports 2. 界面布局XAML <Window x:Class"BlocksTools.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006…

Python 从入门到实战15(字符串其它操作)

我们的目标是&#xff1a;通过这一套资料学习下来&#xff0c;通过熟练掌握python基础&#xff0c;然后结合经典实例、实践相结合&#xff0c;使我们完全掌握python&#xff0c;并做到独立完成项目开发的能力。 上篇文章我们通过举例学习了字符串一些操作说明。今天继续讨论字符…

Java数组08:ArrayList简介

本节内容视频链接&#xff1a; Java关于ArrayList的简单用法与介绍_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1CC4y177CW/?spm_id_from333.337.search-card.all.click&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5Java的ArrayList简介_哔哩哔哩_bilibilihttps:…

Leetcode面试经典150题-27.移除元素

解法都在代码里&#xff0c;不懂就留言或者私信 超级简单的题&#xff0c;一般出现在笔试里&#xff0c;但是不知道为啥字节高频题里会排的那么靠前 class Solution {public int removeElement(int[] nums, int val) {/**如果数组为空&#xff0c;没什么可操作的&#xff0c;…

产业园服务体系建设,是否已全面覆盖企业成长的每一个阶段?

在当今竞争激烈的商业环境中&#xff0c;产业园作为企业发展的重要载体&#xff0c;其服务体系的完善程度至关重要。那么&#xff0c;产业园服务体系建设&#xff0c;是否已全面覆盖企业成长的每一个阶段呢&#xff1f; 从企业的初创期来看&#xff0c;产业园可以提供办公场地的…

【JUC】15-ThreadLocal线程局部变量

1. ThreadLocal ThreadLocal提供线程局部变量。每个线程在访问ThreadLocal实例的时候都有自己的、独立的变量副本。ThreadLocal实例通常是类中的私有静态字段&#xff0c;使用它的目的是希望将状态(用户ID或事务ID)与线程关联起来。 class Saler {ThreadLocal<Integer> …

基于Boost库的搜索引擎开发实践

目录 1.项目相关背景2.宏观原理3.相关技术栈和环境4.正排、倒排索引原理5.去标签和数据清洗模块parser5.1.认识标签5.2.准备数据源5.3.编写数据清洗代码parser5.3.1.编写读取文件Readfile5.3.2.编写分析文件Anafile5.3.2.编写保存清洗后数据SaveHtml5.3.2.测试parser 6.编写索引…

HPM6E00:PWM V2使用指南

先楫推出的HPM6E00系列芯片&#xff0c;PWM功能升级到了V2版本。和V1版本不同的是&#xff0c;V2版本的每组PWM模块包含4个独立的PWM生成模块&#xff0c;每个PWM生成模块包含一个counter和4个比较器&#xff0c;可以生成4组频率不同的PWM波。每个PWM生成模块&#xff0c;对应生…

​​​​通过给定一个全屏的位置得到该位置处是哪一个控件、 遍历窗口中的每一个元素

通过给定一个全屏的位置得到该位置处是哪一个控件&#xff08;以下方法&#xff09; [static] QWidget *QApplication::widgetAt(const QPoint &point) 场景&#xff1a;通过位置获取该位置处的widget后&#xff0c;然后进行判断&#xff0c;是不是某个或某些控件&#x…

韩语中的多义词 (치다)柯桥学韩语到蓝天广场附近

치다 1. 表示用毛笔、铅笔等点点、划线或者绘图。 예: 밑줄을 치다. 划底线 중요한 부분에 동그라미를 쳤다. 在重要的部分画上圆圈。 2. 表示倾倒少量液体或者粉末之类的东西。 예: 싱거우니 소금을 쳐야겠다. 味道淡&#xff0c;得再撒点盐。 기계에 기름을 치다. 给机…

小众创新组合!LightGBM+BO-Transformer-LSTM多变量回归交通流量预测(Matlab)

小众创新组合&#xff01;LightGBMBO-Transformer-LSTM多变量回归交通流量预测(Matlab) 目录 小众创新组合&#xff01;LightGBMBO-Transformer-LSTM多变量回归交通流量预测(Matlab)效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现LightGBMBO-Transformer-L…

陈坤2024行走的力量 走向山野感受距离自然更近的地方

近日&#xff0c;由陈坤发起的心灵建设类项目“行走的力量”在西藏林芝圆满完成&#xff0c;今年陈坤和行者们重返西藏&#xff0c;在海拔3500-4700的高原行走了6天5夜&#xff0c;从城市走向山间&#xff0c;感受自然里的生活&#xff0c;用行走的方式&#xff0c;让自己慢下来…

【C++ Primer Plus习题】15.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream> #include "sales.h"…

第二期: 第三节 裸机代码如何烧写

这个 过程其实 需要在 编写了 驱动之后&#xff0c; 再进行。 因为 要烧写 代码&#xff0c; 你必须 要有一份&#xff0c; 可以烧写的代码。 这里比较重要的是 secureCRT 的安装流程。 这里忘记怎么安装的了。 大致就是 &#xff0c; 先安装 secureCRT , 然后 在破解。不破…