C++学习笔记(十六)

一、多态

1. 多态的基本概念

多态是C++面向对象三大特性之一

多态分为两类

1. 静态多态:函数重载 和 运算符重载属于静态多态,复用函数名

2. 动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态区别:

1. 静态多态的函数地址早绑定 —— 编译阶段确定函数地址

2. 动态多态的函数地址晚绑定 —— 运行阶段确定函数地址

#include <iostream>

using namespace std;

// 动物类
class Animal
{
public:
	// 虚函数
	virtual void speak()
	{
		cout << "动物在叫......" << endl;
	}
};

// 猫类
class Cat :public Animal
{
public:
	void speak()
	{
		cout << "小猫在喵喵叫......" << endl;
	}
};

// 狗类
class Dog :public Animal
{
public:
	void speak()
	{
		cout << "小狗在汪汪叫......" << endl;
	}
};

// 执行叫的函数
// 地址早绑定 在编译阶段就确定了函数地址
// 如果想执行让猫叫或者让狗叫,那么这个函数的地址就不能提前绑定,需要在运行阶段进行绑定,地址晚绑定
void doSpeak(Animal& animal) // Animal& animal = cat;父类的引用指向子类的对象
{
	animal.speak();
}

void test()
{
	Cat cat;
	doSpeak(cat);

	Dog dog;
	doSpeak(dog);
}
int main(int argc, char* argv[])
{
	test();
	return 0;
}

动态多态满足的条件:

1. 有继承关系

2. 子类重写父类的虚函数

动态多态使用:父类的指针或者引用指向子类对象

重写:函数返回值类型、函数名、参数列表完全一致

重载:函数的参数列表不同

2. 多态的原理

在定义了虚函数的类中,都会有一个虚函数表指针,指向该类的虚函数表,表内记录虚函数的地址

当派生类继承基类时,会继承基类的虚函数表指针,指向基类的虚函数表

如果在派生类中实现了重写,先将基类虚表内容拷贝一份至派生类的虚表当中(派生类的虚表是自己生成的,这两个虚表的地址不一样),然后将重写的虚函数的地址放入虚表的对应位置。

3. 多态案例一:计算机类

案例描述:

分别应用普通写法和多态技术,设计实现两个操作数进行运算的计算器

多态的优点:

1. 代码组织结构清晰

2. 可读性强

3. 利于前期和后期的扩展以及维护

普通实现:

#include <iostream>
#include <string>

using namespace std;

class Calculator
{
public:
	double m_num1;
	double m_num2;

	double getResult(string oper)
	{
		if (oper == "+")
			return m_num1 + m_num2;
		else if(oper == "-")
			return m_num1 - m_num2;
		else if (oper == "*")
			return m_num1 * m_num2;
		else if (oper == "/")
			return m_num1 / m_num2;
	}
};

void test()
{
	Calculator c;
	c.m_num1 = 20;
	c.m_num2 = 3.3;

	cout << c.m_num1 << " + " << c.m_num2 << " = " << c.getResult("+") << endl;
	cout << c.m_num1 << " - " << c.m_num2 << " = " << c.getResult("-") << endl;
	cout << c.m_num1 << " * " << c.m_num2 << " = " << c.getResult("*") << endl;
	cout << c.m_num1 << " / " << c.m_num2 << " = " << c.getResult("/") << endl;
}
int main(int argc, char* argv[])
{
	test();
	return 0;
}

如果想扩展新的功能,需要修改源码

在真实的开发中,提供开闭原则

开闭原则:对扩展进行开放,对修改进行关闭

#include <iostream>
#include <string>

using namespace std;

// 实现计算器基类
class BaseCalculator
{
public:
	double m_num1;
	double m_num2;

	virtual double getResult()
	{
		return 0;
	}
};

// 加法计算器类
class AddCalculator : public BaseCalculator
{
public:
	double getResult()
	{
		return m_num1 + m_num2;
	}
};

// 减法计算器类
class SubCalculator : public BaseCalculator
{
public:
	double getResult()
	{
		return m_num1 - m_num2;
	}
};

// 乘法计算器类
class MulCalculator : public BaseCalculator
{
public:
	double getResult()
	{
		return m_num1 * m_num2;
	}
};

// 除法计算器类
class DivCalculator : public BaseCalculator
{
public:
	double getResult()
	{
		return m_num1 / m_num2;
	}
};

void test()
{
	// 多态使用条件
	// 父类指针或者引用指向子类对象

	// 加法运算
	BaseCalculator *calculator = new AddCalculator;
	calculator->m_num1 = 20;
	calculator->m_num2 = 3.3;
	cout << calculator->m_num1 << " + " << calculator->m_num2 << " = " << calculator->getResult() << endl;
	// 堆区数据手动开辟手动释放
	delete calculator;

	// 减法运算
	calculator = new SubCalculator;
	calculator->m_num1 = 20;
	calculator->m_num2 = 3.3;
	cout << calculator->m_num1 << " - " << calculator->m_num2 << " = " << calculator->getResult() << endl;
	delete calculator;

	// 乘法运算
	calculator = new MulCalculator;
	calculator->m_num1 = 20;
	calculator->m_num2 = 3.3;
	cout << calculator->m_num1 << " * " << calculator->m_num2 << " = " << calculator->getResult() << endl;
	delete calculator;

	// 除法运算
	calculator = new DivCalculator;
	calculator->m_num1 = 20;
	calculator->m_num2 = 3.3;
	cout << calculator->m_num1 << " / " << calculator->m_num2 << " = " << calculator->getResult() << endl;
	delete calculator;
}
int main(int argc, char* argv[])
{
	test();
	return 0;
}

4. 纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容

因此可以将虚函数改为纯虚函数

纯虚函数语法:virtual 返回值类型 函数名 (参数列表)= 0;

当类中有了纯虚函数,这个类也称为抽象类

抽象类的特点:

1. 无法实例化对象

2. 子类必须重写抽象类中的纯虚函数,否则也属于抽象类

#include <iostream>

using namespace std;

class Base
{
public:
	virtual void func() = 0;
};

class Son1 : public Base
{
	void func()
	{
		cout << "已对父类的的纯虚函数进行重写..." << endl;
	}
};

class Son2 : public Base
{
public:

};

void test()
{
	// 无法对抽象类进行实例化
	// Base b;
	
	Base* ptr1 = new Son1;
	ptr1->func();
	
	// 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
	// Base* ptr2 = new Son2;
}

int main(int argc, char* argv[])
{
	test();
	return 0;
}

5. 多态案例二:制作饮品

案例描述:制作饮品的大致流程为:煮水 -> 冲泡(咖啡、茶叶) -> 倒入杯中 -> 加入辅料(糖、柠檬)

利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶

#include <iostream>

using namespace std;

class AbstractDrinking
{
public:
	void Boil()
	{
		cout << "煮水中..." << endl;
	}
	virtual void Brew() = 0;
	void PourInCup()
	{
		cout << "正在倒入杯中..." << endl;
	}
	virtual void PutSomething() = 0;
	void Done()
	{
		cout << "制作完成..." << endl;
	}
	void makeDrink()
	{
		Boil();
		Brew();
		PourInCup();
		PutSomething();
		Done();
	}
};

class Coffee : public AbstractDrinking
{
public:
	void Brew()
	{
		cout << "正在冲泡咖啡..." << endl;
	}
	void PutSomething()
	{
		cout << "正在加入糖..." << endl;
	}
};

class Tea : public AbstractDrinking
{
public:
	void Brew()
	{
		cout << "正在冲泡茶叶..." << endl;
	}
	void PutSomething()
	{
		cout << "正在加入枸杞..." << endl;
	}
};

void doWork(AbstractDrinking *abs)
{
	abs->makeDrink();
	delete abs;
}

void test()
{
	// 制作咖啡
	doWork(new Coffee);
	
	cout << "-------------------" << endl;

	// 制作茶
	doWork(new Tea);
}
int main(int argc, char* argv[])
{
	test();
	return 0;
}

6. 虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到了堆区,那么父类指针在释放时无法调用到子类的析构代码

解决方式:将父类中的析构函数改为虚析构或者纯虚析构

虚析构和纯虚析构的共性:

        1. 可以解决父类指针释放子类对象

        2. 都需要有具体的函数实现

虚析构和纯虚析构的区别:

        1. 如果时纯虚析构,该类属于抽象类,无法实例化对象

虚析构语法:virtual ~类名( ){ }

纯虚析构语法:需要声明和实现

                      virtual ~类名() = 0;

                      类名::~类名( ){ }

#include <iostream>

using namespace std;

class Base
{
public:
	virtual void func() = 0;
	Base()
	{
		cout << "基类的构造函数已调用..." << endl;
	}
	~Base()
	{
		cout << "基类的析构函数已调用..." << endl;
	}
};

class Son : public Base
{
public:
	void func()
	{
		cout << "派生类中重写的纯虚函数已调用..." << endl;
	}
	Son()
	{
		cout << "派生类的构造函数已调用..." << endl;
	}
	~Son()
	{
		cout << "派生类的析构函数已调用..." << endl;
	}
};

void test()
{
	Base* ptr = new Son;
	ptr->func();
	delete ptr;
}

int main(int argc, char* argv[])
{
	test();
	return 0;
}

从运行结果可以看出,父类指针在析构时,不会调用子类中的析构函数,如果子类中有堆区属性,就会出现内存泄漏的问题

利用虚析构可以解决父类指针释放子类对象时不干净的问题

#include <iostream>

using namespace std;

class Base
{
public:
	virtual void func() = 0;
	Base()
	{
		cout << "基类的构造函数已调用..." << endl;
	}
	virtual ~Base()
	{
		cout << "基类的析构函数已调用..." << endl;
	}
};

class Son : public Base
{
public:
	void func()
	{
		cout << "派生类中重写的纯虚函数已调用..." << endl;
	}
	Son()
	{
		cout << "派生类的构造函数已调用..." << endl;
	}
	~Son()
	{
		cout << "派生类的析构函数已调用..." << endl;
	}
};

void test()
{
	Base* ptr = new Son;
	ptr->func();
	delete ptr;
}

int main(int argc, char* argv[])
{
	test();
	return 0;
}

纯虚析构也可以解决这个问题,但是要注意,纯虚析构不同于纯虚函数,纯虚析构需要类内声明类外实现

#include <iostream>

using namespace std;

class Base
{
public:
	virtual void func() = 0;
	Base()
	{
		cout << "基类的构造函数已调用..." << endl;
	}
	virtual ~Base() = 0;
};
Base::~Base()
{
	cout << "基类的纯虚析构函数已调用..." << endl;
}

class Son : public Base
{
public:
	void func()
	{
		cout << "派生类中重写的纯虚函数已调用..." << endl;
	}
	Son()
	{
		cout << "派生类的构造函数已调用..." << endl;
	}
	~Son()
	{
		cout << "派生类的析构函数已调用..." << endl;
	}
};

void test()
{
	Base* ptr = new Son;
	ptr->func();
	delete ptr;
}

int main(int argc, char* argv[])
{
	test();
	return 0;
}

总结:

1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象

2. 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构

3. 拥有纯虚析构函数的类也属于抽象类

7. 多态案例三:电脑组装

案例描述:

电脑主要组成部件为CPU(用于计算),显卡(用于显示),内存条(用于存储)

将每个零件封装出抽象类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商

创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口

测试时组装三台不同的电脑进行工作

#include <iostream>

using namespace std;

class CPU
{
public:
	virtual void calculate() = 0;
};

class VideoCard
{
public:
	virtual void show() = 0;
};

class Memory
{
public:
	virtual void store() = 0;
};

class IntelCPU : public CPU
{
public:
	void calculate()
	{
		cout << "计算能力:10" << endl;
	}
};

class IntelVideoCard : public VideoCard
{
public:
	void show()
	{
		cout << "显示能力:7" << endl;
	}
};

class IntelMemory : public Memory
{
public:
	void store()
	{
		cout << "存储能力:8" << endl;
	}
};

class LenovoCPU : public CPU
{
public:
	void calculate()
	{
		cout << "计算能力:9" << endl;
	}
};

class LenovoVideoCard : public VideoCard
{
public:
	void show()
	{
		cout << "显示能力:6" << endl;
	}
};

class LenovoMemory : public Memory
{
public:
	void store()
	{
		cout << "存储能力:9" << endl;
	}
};

class Computer
{
public:
	Computer(CPU* CPU, VideoCard* vc, Memory* mem)
	{
		m_CPU = CPU;
		m_vc = vc;
		m_mem = mem;
	}
	void work()
	{
		m_CPU->calculate();
		m_vc->show();
		m_mem->store();
	}
	~Computer()
	{
		if (m_CPU != NULL)
		{
			delete m_CPU;
			m_CPU = NULL;
		}
		if (m_vc != NULL)
		{
			delete m_vc;
			m_vc = NULL;
		}
		if (m_mem != NULL)
		{
			delete m_mem;
			m_mem = NULL;
		}
	}
private:
	CPU* m_CPU;
	VideoCard* m_vc;
	Memory* m_mem;
};

void test()
{
	// 第一台电脑
	cout << "--------第一台电脑--------" << endl;
	CPU* First_CPU = new IntelCPU;
	VideoCard* First_VideoCard = new IntelVideoCard;
	Memory* First_Memory = new IntelMemory;
	Computer* c1 = new Computer(First_CPU, First_VideoCard, First_Memory);
	c1->work();
	delete c1;
	
	// 第二台电脑
	cout << "--------第二台电脑--------" << endl;
	CPU* Second_CPU = new LenovoCPU;
	VideoCard* Second_VideoCard = new LenovoVideoCard;
	Memory* Second_Memory = new LenovoMemory;
	Computer* c2 = new Computer(Second_CPU, Second_VideoCard, Second_Memory);
	c2->work();
	delete c2;

	// 第三台电脑
	cout << "--------第三台电脑--------" << endl;
	CPU* Third_CPU = new IntelCPU;
	VideoCard* Third_VideoCard = new LenovoVideoCard;
	Memory* Third_Memory = new IntelMemory;
	Computer* c3 = new Computer(Third_CPU, Third_VideoCard, Third_Memory);
	c3->work();
	delete c3;

}
int main(int argc, char* argv[])
{
	test();
	return 0;
}

二、文件操作

程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放

通过文件可以将数据持久化

C++中对文件操作需要包含头文件 <fstream>

文件类型分为两种:

1. 文本文件:文件以文本的ASCLL码形式存储在计算机中

2. 二进制文件:文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂他们

操作文件的三大类:

1. ofstream:写操作

2. ifstream:读操作

3. fstream:读写操作

1. 文本文件

1.1 写文件

写文件步骤如下:

1. 包括头文件

        # include <fstream>

2. 创建流对象

        ofstream ofs;

3. 打开文件

        ofs.open("文件路径",打开方式);

4. 写数据

        ofs << "写入的数据";

5. 关闭文件

        ofs.close();

文件打开方式:

打开方式解释
ios::in为读文件而打开文件
ios::out为写文件而打开文件
ios::ate初始位置:文件尾
ios::app追加方式写文件
ios::trunc如果文件存在先删除,再创建
ios::binary二进制方式

注意:文件打开方式可以配合使用,利用 | 操作符

例如:用二进制方式写文件: ios::binary | ios::out

#include <iostream>
#include <fstream>

using namespace std;


int main(int argc, char* argv[])
{
	// 1. 包含头文件 fstream

	// 2. 创建流对象
	ofstream ofs;

	// 3. 指定打开方式
	ofs.open("D:\\test.txt", ios::out);

	// 4. 写内容
	ofs << "姓名:张三" << endl;
	ofs << "年龄:23" << endl;
	ofs << "性别:男" << endl;
	ofs << "居住地:地球" << endl;

	// 5. 关闭文件
	ofs.close();
	return 0;
}

总结:

1. 文件操作必须包含头文件 fstream

2. 读文件可以利用ofstream或者fstream类

3. 打开文件时需要指定操作文件的路径,以及打开方式

4. 利用 << 可以向文件中写数据

5. 操作完成后,需要关闭文件

1.2 读文件

读文件与写文件步骤相似,但是读取方式相对比较多

读文件步骤如下:

1. 包含头文件

        # include <fstream>

2. 创建流对象

        ifstream ifs;

3. 打开文件并判断文件是否打开成功

        ifs.open("文件路径",打开方式)

4. 读数据

        四种方式读取

5. 关闭文件

        ifs.close()

#include <iostream>
#include <fstream>
#include <string>

using namespace std;


void main(int argc, char* argv[])
{
	// 1. 包含头文件 fstream

	// 2. 创建流对象
	ifstream ifs;

	// 3. 指定打开方式
	ifs.open("D:\\test.txt", ios::in);

	// 判断文件是否打开成功
	if (!ifs.is_open())
	{
		cout << "文件打开失败..." << endl;
		return;
	}

	// 4. 读内容
	// (1).第一种
	char buffer[1024] = { 0 };
	while (ifs >> buffer)
	{
		cout << buffer << endl;
	}
	// (2).第二种
	char buffer[1024] = { 0 };
	while (ifs.getline(buffer,sizeof(buffer)))
	{
		cout << buffer << endl;
	}
	// (3).第三种
	string buffer;
	while (getline(ifs,buffer))
	{
		cout << buffer << endl;
	}
	// (4).第四种
	char c;
	while ((c = ifs.get()) != EOF)
	{
		cout << c;
	}

	// 5. 关闭文件
	ifs.close();
}

2. 二进制文件

以二进制的方式对文件进行读写操作

打开方式要指定为 ios::binary

2.1 写文件

二进制方式写文件主要利用流对象调用成员函数 write

函数原型:ostream& write(const char * buffer, int len);

参数解释:字符指针buffer指向内存中一段存储空间。len时读写的字节数

#include <iostream>
#include <fstream>

using namespace std;

class Person
{
public:
	char m_Name[64];
	int m_Age;
};

void test()
{
	// 1. 包含头文件 fstream

	// 2. 创建流对象
	ofstream ofs;

	// 3. 指定打开方式
	ofs.open("D:\\test.txt", ios::out | ios::binary);

	// 4. 写内容
	Person p = { "张三",18 };
	ofs.write((const char*)&p, sizeof(Person));

	// 5. 关闭文件
	ofs.close();
}

int main(int argc, char* argv[])
{
	test();
	return 0;
}

总结:

文件输出流对象可以通过write函数,以二进制方式写数据

2.2 读文件

二进制方式读文件主要利用流对象调用成员函数read

函数原型:istream& read(char * buffer, int len);

参数解释:字符串指针buffer指向内存中一段存储空间。len时读写的字节数

#include <iostream>
#include <fstream>

using namespace std;

class Person
{
public:
	char m_Name[64];
	int m_Age;
};

void test()
{
	// 1. 包含头文件 fstream

	// 2. 创建流对象
	ifstream ifs;

	// 3. 指定打开方式
	ifs.open("D:\\test.txt", ios::in | ios::binary);

	// 判断文件是否打开成功
	if (!ifs.is_open())
	{
		cout << "文件打开失败..." << endl;
		return;
	}
	// 4. 写内容
	Person p;
	ifs.read((char*)&p, sizeof(Person));
	cout << "姓名:" << p.m_Name << endl;
	cout << "年龄:" << p.m_Age << endl;

	// 5. 关闭文件
	ifs.close();
}

int main(int argc, char* argv[])
{
	test();
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32单片机项目实例:基于TouchGFX的智能手表设计(5)硬件驱动层程序设计

STM32单片机项目实例&#xff1a;基于TouchGFX的智能手表设计&#xff08;5&#xff09;硬件驱动层程序设计 目录 一、 概述 二、 新建工程与外设配置 三、 TouchGFX配置 四、 增加TouchGFX关键驱动 一、 概述 本文内容主要进行工程新建&#xff0c;硬件外设的配置以及添加…

用webstorm学习Vue的时候提示未解析的变量或类型怎么办?

用webstorm学习Vue的时候&#xff0c;很多同学是不是对以下这种提示看着不舒服 这种提示因为webstorm在解析代码进行提示的时候会把html文件中css选择器进行缓存&#xff0c;如果你在同一个文件夹下面写很多个html&#xff0c;并且多个html中都有同样的<div id"root&qu…

用23种设计模式打造一个cocos creator的游戏框架----(二十一)组合模式

1、模式标准 模式名称&#xff1a;组合模式 模式分类&#xff1a;结构型 模式意图&#xff1a;将对象组合成树型结构以表示“部分-整体”的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性。 结构图&#xff1a; 适用于&#xff1a; 1、想表示对象的部分…

RK3399平台开发系列讲解(内核入门篇)网络协议的分层

🚀返回专栏总目录 文章目录 一、应用层二、传输层三、网络层四、数据链路层(Data Link Layer)五、物理层沉淀、分享、成长,让自己和他人都能有所收获!😄 📢对于多数的应用和用户而言,使用互联网的一个基本要求就是数据可以无损地到达。用户通过应用进行网络通信࿰

leetcode每日一题打卡

leetcode每日一题 746.使用最小花费爬楼梯162.寻找峰值1901.寻找峰值Ⅱ 从2023年12月17日开始打卡~持续更新 746.使用最小花费爬楼梯 2023/12/17 代码 解法一 class Solution {public int minCostClimbingStairs(int[] cost) {int n cost.length;int[] dp new int[n1];dp[…

《每天一分钟学习C语言·一》

1、转义字符&#xff1a;\n换行&#xff0c;\t前进一个tab键&#xff0c;\b退格键 2、八进制前面有0&#xff0c;%o或者%#o表示八进制&#xff0c;十六进制前有0X&#xff0c;%0x或者%#0x表示十六进制 3、%u打印无符号数&#xff0c;%g显示小数&#xff0c;类似于%f&#xff…

cleanmymac有必要买吗 macbook空间不足怎么办 清理macbook磁盘空间

大家早上好&#xff0c;中午好&#xff0c;下午好&#xff0c;晚上好。 文章有点长&#xff0c;建议先收藏。 macbook是一款非常受欢迎的笔记本电脑&#xff0c;它拥有优秀的性能和设计&#xff0c;但是也有一个常见的问题&#xff0c;就是磁盘空间不足。如果你的macbook经常…

python之pyQt5实例:二维码生成与读取

目录 1、显示逻辑 2、业务逻辑 二维码的产生历史可以追溯到20世纪80年代。当时&#xff0c;日本Denso Wave公司为了追踪汽车零部件的运输和销售情况&#xff0c;开发了一种能够存储大量信息的条形码&#xff0c;这就是二维码的前身。到了20世纪90年代&#xff0c;随着手机和照…

SG3524控制的恒流源电路图

SG3524简介 SG3524是开关电源脉宽调制型控制器。应用于开关稳压器&#xff0c;变压器耦合的直流变换器&#xff0c;电压倍增器&#xff0c;极性转换器等。采用固定频率&#xff0c;脉冲宽度调制&#xff08;脉宽调制&#xff09;技术。输出允许单端或推挽输出。芯片电路包括电…

20、WEB攻防——PHP特性缺陷对比函数CTF考点CMS审计实例

文章目录 一、PHP常用过滤函数&#xff1a;1.1 与1.2 md51.3 intval1.4 strpos1.5 in_array1.6 preg_match1.7 str_replace CTFshow演示三、参考资料 一、PHP常用过滤函数&#xff1a; 1.1 与 &#xff1a;弱类型对比&#xff08;不考虑数据类型&#xff09;&#xff0c;甚至…

创建个人网站(二)前端主页设计和编写一(太阳移动)

前言 以下内容纯纯当乐子来看就行&#xff0c;知识分享一下这样设计的原因&#xff0c;想看正文直接见下一节 为什么创建个人网站一之后几天没有动静了呢&#xff0c;一个是家里有事实在比较忙&#xff0c;第二个原因是没想到主页要设计成什么样&#xff0c;知道前两天问我姐什…

Labview Vision 机器视觉使用,从下载程序安装应用,到实战找硬币并输出值

1.前言 大家好,今天我要和机器人一起配合来打算 做机器视觉 用Labview 和 Vision 联动实现机器的视觉 2.下载软件-软件的安装 我们除了基础款的labview软件 还要安装视觉四件套 1.Labview 编程平台&#xff08;我是 2023 q3&#xff09; 2. NI - IMAQdx &#xff08;驱动软…

客户关系管理系统 crm

系统开发环境以及版本 操作系统&#xff1a; Windows_7集成开发工具&#xff1a; Eclipse EE_4.7编译环境&#xff1a;JDK_1.8Web服务器&#xff1a;Tomcat_9.0数据库&#xff1a;MySQL_5.7.23 系统框架 spring框架springmvc框架mybatis框架Logback日志框安全验证框架maven框架…

数据库增删改查Native SQL

DBCO&#xff1a;检查数据库是否连接 代码&#xff1a; 查询&#xff1a; DATA: gv_dbs TYPE char30 VALUE XXXXXXXX. "数据库连接名称 DATA:gt_ztclaim_2 TYPE TABLE OF ztclaim_2. DATA:gs_ztclaim_2 TYPE ztclaim_2.TRY.EXEC SQL.CONNECT TO :GV_DBSENDEXEC.EXEC SQ…

速度与稳定性的完美结合:深入横测ToDesk、TeamViewer和AnyDesk

文章目录 前言什么是远程办公&#xff1f;远程办公的优势 远程办公软件横测对象远程软件的注册&安装ToDeskTeamViewerAnyDesk 各场景下的实操体验1.办公文件传输及丢包率2.玩游戏操作延迟、稳定3.追剧画质流畅度、稳定4.临时技术支持SOS模式 收费情况与设备连接数总结 前言…

【兔子王赠书第13期】AI绘画实战:Midjourney从新手到高手

文章目录 写在前面AI绘画推荐图书一本书读懂AI绘画关键点内容简介作者简介 推荐理由粉丝福利写在后面 写在前面 如今AI技术已经进入了我们的日常学习生活中&#xff0c;如何用一本书轻松玩转AI绘画&#xff0c;领略无限艺术可能呢&#xff1f; AI绘画 AI绘画是指利用人工智能…

【python】深拷贝和浅拷贝

能使用.copy()的对象&#xff1a; 需要是能改变元素的对象比如 list 和 set 还有 字典dic就可以改变对象&#xff0c;可以使用copy函数但是类似于 一个整数 a10 或者 元组 或者字符串就不能使用copy函数&#xff0c;因为他们是不可改变的对象 深拷贝和浅拷贝 浅拷贝就是这能复…

响应式布局2:手写响应式导航栏(BootStrap实现以及原生实现)

1.响应式导航栏介绍 响应式导航栏是一种在不同设备和屏幕尺寸下自适应布局和显示的导航栏。它可以根据用户所使用的设备&#xff08;如桌面电脑、平板电脑或手机&#xff09;自动调整其外观和交互方式&#xff0c;以提供更好的用户体验。 pc端&#xff1a; 手机端&#xff1a…

如何打造自己的知识付费小程序平台

在当今知识付费的浪潮中&#xff0c;我们经常可以看到各种知识付费平台如雨后春笋般涌现。然而&#xff0c;这些平台往往只是一个过客&#xff0c;让我们短暂停留后&#xff0c;便淹没在信息的海洋中。如果你有一个出色的课程&#xff0c;为什么不让它在一个属于你自己的平台上…

TypeError: Cannot read properties of undefined (reading ‘row‘)原因及解决办法

TypeError: Cannot read properties of undefined (reading ‘row’) 这个错误表明 Vue 在渲染时试图读取未定义或空值的属性&#xff0c;导致无法访问到属性 ‘row’。需要加入插槽slot-scope“scope”