C++类和对象(基础篇)

news2025/7/17 8:32:28

前言:

        其实任何东西,只要你想学,没人能挡得住你,而且其实学的也很快。那么本篇开始学习类和对象(C++的,由于作者有Java基础,可能有些东西过得很快)。

struct在C++中的含义:

        C++兼容C中的struct的用法,并将其升级,可以在里面写函数。

// C++兼容C中的struct的用法
// C++升级struct升级成了类
// 类和对象
// 1个类 实例化 N个对象

// 1.类里面可以定义函数
// 2.struct名称可以代表类型
struct Stack
{
	//成员函数
	void Init(int n = 4)
	{
		_arry = (int*)malloc(sizeof(int) * n);
		if (nullptr == _arry)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}

	//成员变量
	int* _arry;
	size_t _capacity;
	size_t _top;
};

int main()
{
	struct Stack s1;


	Stack s;
	s.Init();
	return 0;
}

        我们可以不用再写 struct + 结构体名称 去创建对象,相当于已经typedef了,并且可以直接在通过对象调用函数。

        这也体现了C++兼容C的特性。但是C++还是一般并不是用这种用法,一般是使用class来声明一个类。

class Stack
{
	void Init(int n = 4)
	{
		_arry = (int*)malloc(sizeof(int) * n);
		if (nullptr == _arry)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}

	//成员变量
	int* _arry;
	size_t _capacity;
	size_t _top;
};

int main()
{
	Stack s;
	//此时我们直接去使用里面的Init方法,发现报错
	s.Init();
	return 0;
}

访问权限: 

        上面我们看到报错,这是为啥?这是因为C++中有访问限定符。

1.public修饰的成员在类外可以直接访问

2.protected和private修饰的成员在类外不能直接被访问(此处protected 和 private 是类似的)

3.访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止

4.如果后面没有访问限定符,作用域就到 } 即类结束

5.class的默认访问权限为 private ,struct 为 public (因为 struct 要兼容C)

                所以报错的原因是因为前面默认被 private 修饰了。 

class Stack
{
public:
	void Init(int n = 4)
	{
		_arry = (int*)malloc(sizeof(int) * n);
		if (nullptr == _arry)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}

private:
	//成员变量
	int* _arry;
	size_t _capacity;
	size_t _top;
};

        所以一般是成员变量私有,成员方法公有。

        C++建议成员变量前面加_(这是软性建议)。

        我们先创建一个Stack.h头文件,并在里面写入:

Stack.h头文件(声明函数):

#include<iostream>

class Stack
{
public:
	void Init();
private:
	int* _a;
	int* _top;
	int* _capacity;
};

        此时我们在Stack.h头文件中定义一个类并声明其中的一个函数(没有定义),之后在Stack.cpp源文件中定义该函数,此时我们就需要指定类域去定义该函数。因为该函数并不是全局变量,必须指定类域。

Stack.cpp源文件中(定义函数):

void Stack::Init()
{
	_a = nullptr;
	_top = 0;
	_capacity = 0;
}

test.c源文件中(调用函数): 

int main()
{
	Stack st1;
	cout << sizeof(st1) << endl;
	cout << sizeof(Stack) << endl;
	return 0;
}

        可以发现没有计算函数的大小,因为实例化的对象调用的函数地址都是一样的,每个空间放一份,会造成空间的浪费。 

        所以对象占用的大小,只考虑成员变量,其大小规则和struct类似。

        对于一个空类(就是里面没有任何成员变量),空类的大小为1字节,以确保每个实例都有独一无二的地址,不储存有效数据,标识对象被定义出来了。

this指针:

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1, d2;
	d1.Init(1990, 12, 1);
	d2.Init(1991, 12, 1);
	d1.Print(), d2.Print();
	return 0;
}

        这里有一个奇怪的现象,为什么打印的不同,它怎么会知道谁传给了我?其实这里的Print方法中有一个隐含的参数-this,这个this是一个指针。

void Print(Date* this)

        之后只要是成员变联名,它自动识别到了,都相当于前面加上了this指针。

void Print()
{
	cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
}

        所以其实函数相当于是这样使用的,但是默认不加。所以我们还可以这样调用: 

d1.Print(&d1);

        严格来说是这样写的:

void Print(Date* const this)

注意事项一:

        我们先来观察以下代码:

class A
{
public:
	void Print()
	{
		cout << "Print()" << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

        这里是其实没有对nullptr去进行解引用,因为默认传入this指针,并不影响对Print函数的使用。 

注意事项二:

        再来观察一下代码:

class A
{
public:
	void Print()
	{
		cout << _a << endl;
	}
private:
	int _a;
};

int main()
{
	A* p = nullptr;
	p->Print();
	return 0;
}

        但这个结果是运行崩溃。我们来观察他们的区别: 

        this指针存在栈中,因为它是形参,也有可能是寄存器。

类的默认成员函数:

        类有6个默认成员函数,编译器会自动生成6个默认成员函数(很神奇,竟然有6个)。

        默认成员函数:用户没有显示实现,编译器会生成的成员函数成为默认成员函数。

构造函数:

        函数名与类名相同,无返回值,对象实例化编译器自动调用对应的构造函数,构造函数可以重载。分为无参构造和有参构造,类似于初始化函数。和Java使用方式是一样的,不赘述。

        当我们实例化一个对象后,会自动调用构造函数。

class Date
{
public:
	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}

	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
	void Print()
	{
		cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
		this->Fanacy();
	}

	void Fanacy()
	{
		cout << "hehe" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	d1.Print();
	return 0;
}

        观察以下代码:

Date()
{
	_year = 1;
	_month = 1;
	_day = 1;
}

Date(int year, int month, int day)
{
	this->_year = year;
	this->_month = month;
	this->_day = day;
}

        此时我们有两个构造函数,一个有参一个无参,并通过主函数这样调用:

int main()
{
	Date d1;
	Date d2(2, 3, 4);
	d1.Print();
	d2.Print();
	return 0;
}

        如果是无参的不要带括号,只是为了和函数声明区分开。 

Date d1;
Date d2(2, 3, 4);

Date d1();//函数声明

        构造函数一般喜欢写成全缺省函数。

Date(int year = 1, int month = 1, int day = 1)
{
	this->_year = year;
	this->_month = month;
	this->_day = day;
}

        这样就可以把无参构造省略了,非常方便。 

        记住缺省函数定义和声明不能同时给,规定只能在声明的时候给,定义的时候不给。

        如果我们写了显示的默认成员函数,那么编译器则不会在生成默认成员函数,也是和Java一样,救急不救穷。

        编译器自动生成的构造函数,对于内置类型成员变量不做处理(也就是int了,double了等等基础类型),当然有些编译器会处理。

        不传参数就可以调用的函数就是默认构造。

        C++11为了解决初始化问题,我们可以在定义成员变量时给定缺省值。

class Date
{
public:
	void Print()
	{
		cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
	}

private:
	//给缺省值
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

int main()
{
	Date d1;
	d1.Print();
	return 0;
}

        默认构造函数只能有一个。注意:无参构造函数、全缺省函数、编译器默认生成构造函数,都可以认为是默认构造函数。

        3个只能存在一个。

析构函数:

        析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作使用编译器完成的。而对象在销毁是会自动调用析构函数,完成对象中资源的清理工作。

        其也是一个特殊的成员函数,特征如下:

        析构函数名是在类前加上字符~;无参数无返回类型;一个类只能有一个析构函数。若未显示定义,系统会自动生成默认的析构函数。注意:析构函数不能重载;对象生命周期结束时,C++编译系统自动调用析构函数。

~Date()
{
	cout << "这是显示调用的析构函数!" << endl;
}

        析构函数我们也可以显示调用。 

d1.~Date();

        不是所有类都需要写析构函数,因为不需要释放空间。

        编译器默认的析构函数对内置类型不做处理,对自定义类型去调用它的析构函数。

拷贝构造:

        拷贝构造也是特殊的成员函数,特征如下:

        拷贝构造函数是构造函数的一个重载形式;拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

        若未显示定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝或者值拷贝。

class A
{
public:
	A(int a = 100)
	{
		_a = a;
	}

	A(A& a)
	{
		_a = a._a;
		cout << "这里调用了拷贝构造!" << endl;
	}

	void Print()
	{
		cout << _a << endl;
	}
private:
	int _a;
};

void func(A a)
{
	a.Print();
}

int main()
{
	A a1(5);
	func(a1);
	return 0;
}

        可以看到,我们并没有去调用任何的拷贝构造参数,但是调用了func函数之后却调用了。

        这是因为这是传值传参,自定义类型对象传值传参要调用拷贝构造。 除非传入指针或者使用引用才不会调用拷贝构造。

        这样就会引起死递归。所以要修改为引用: 

A(const A& a)
{
	_a = a._a;
	cout << "这里调用了拷贝构造!" << endl;
}

        我们一般习惯往里面写入const,是为了防止修改被拷贝的内容。

        这里相当于权限的缩小。

// 下面的两种写法是等价的
A a2(a1);
A a3 = a2; //这也是拷贝构造

        默认构造会实现浅拷贝,也就是不会拷贝引用类型。 默认构造会实现浅拷贝,也就是内置类型的拷贝。

总结:

        如果没有管理资源,一般不要写拷贝构造,默认生成的即可使用;如果都是自定义类型成员,内置类型成员没有指向资源,也类似默认生成的拷贝构造就可以(比如用栈实现队列);一般情况下,不需要显示写析构函数,就不需要写拷贝构造;如果内部有指针或者一些值指向资源,需要显示写析构,通常就需要显示写拷贝构造完成深拷贝。

赋值运算符重载函数: 

        赋值运算符重载是默认成员函数,不写编译器默认生成。用户没有显示实现时,会以值的方式逐字节拷贝。和拷贝构造函数类似。

        复制运算符重载不能写成全局函数,这是规定!

运算符重载:

        运算符重载:是具有特殊函数名的函数,也具有其返回值类型,函数名及参数和返回值与普通函数类似。

        函数名字为:关键字operator后面需要重载运算符符号。

class Date 
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}

	Date(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	void Print()
	{
		cout << this->_year << "-" << this->_month << "-" << this->_day << endl;
	}

//private:
	//给缺省值
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

//大于
bool Compare1(const Date& d1, const Date& d2)
{
	if (d1._year > d2._year)
	{//年大就大
		return true;
	} 
	else if (d1._year == d2._year)
	{
		if (d1._month > d1._month)
		{//月大就大
			return true;
		}
		else if (d1._month == d2._month)
		{//天大就大
			return d1._day > d2._day;
		}
	}
}

//小于
bool Compare2(const Date& d1, const Date& d2)
{
	if (d1._year < d2._year)
	{//年小就小
		return true;
	}
	else if (d1._year == d2._year)
	{
		if (d1._month < d1._month)
		{//月小就小
			return true;
		}
		else if (d1._month == d2._month)
		{//天小就小
			return d1._day < d2._day;
		}
	}
}

//等于
bool Compare1(const Date& d1, const Date& d2)
{
	if (d1._day == d2._day && d1._year == d2._year && d1._month == d1._month)
	{
		return true;
	}
	return false;
}

int main()
{
	Date d1(2024, 4, 10);
	Date d2(2024, 4, 9);
	cout << Compare1(d1, d2) << endl;

	//cout << (d1 > d2) << endl;
	return 0;
}

        比如此时我对日期类进行比较,写了3个函数来实现,这样很麻烦,但是有操作符重载就会很方便。

cout << (d1 > d2) << endl;

        比如此时我们就像通过这样来完成这个比较方法。 

        重载操作符必须有一个类类型参数,也就是说不能这样写:

//运算符重载是函数,以下形式非法!
int operator-(int i, int j)

        .* :: sizefof  ? :  . 这5个运算符不能重载。 

//大于
bool operator>(const Date& d1, const Date& d2)
{
	if (d1._year > d2._year)
	{//年大就大
		return true;
	}
	else if (d1._year == d2._year)
	{
		if (d1._month > d1._month)
		{//月大就大
			return true;
		}
		else if (d1._month == d2._month)
		{//天大就大
			return d1._day > d2._day;
		}
	}
}

        之后可以直接这样使用:

cout << (d1 > d2) << endl;

        插一嘴,其中.*是一个运算符(蒙了吧,我也蒙了,解释一下)。

class OB
{
public:
	void func()
	{
		cout << "func:" << endl;
	}
};

//重命名函数指针
typedef void(OB::* Ptr)();//重命名

int main()
{
	//这里必须使用&
	Ptr fp = &OB::func;
	OB tmp;
	//此时也就用到了.*这个操作符
	(tmp.*fp)();//通过函数指针调用函数
	return 0;
}

        这里我们使用到了.*。

        一个类要重载哪些元素符是看需求。

class Date 
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}

	bool operator==(const Date& d)
	{
		return _day == d._day
			&& _year == d._year
			&& _month == d._month;
	}

private:
	//给缺省值
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

int main()
{
	Date d1(20, 1, 3);
	Date d2(20, 1, 4);
	//显示调用
	d1.operator==(d2);//这样就没有优势了

	//直接写,转换调用,编译器会转换为 operator==(d1, d2)
	d1 == d2;
	return 0;
}

        此时我们发现,我都是在类外定义的函数,也就意味着要把类中的成员变量都变为公有的才能这样写。 

        有3种解决方法:提供这些成员get和set方法;友元(后期讲);重载为成员函数。

        相信各位对get和set方法都了解,我们不再赘述。

class A
{
public:
	A(int a = 100)
	{
		_a = a;
	}

	A(const A& a)
	{
		_a = a._a;
		cout << "这里调用了拷贝构造!" << endl;
	}

	A operator=(const A& a)
	{//这里面最好加上返回值,这样就可以连续赋值并且不会出错
		_a = a._a;
		return *this;
	}

	void Print()
	{
		cout << _a << endl;
	}
private:
	int _a;
};

int main()
{
	A a1(100);
	A a2;//拷贝一个
	A a3;

	a3 = a2 = a1;
	a3.Print();
	return 0;
}

        我们在使用操作符重载的时候,最好加上返回值,这样可以连续赋值不会出错。

注意事项:        

class Date 
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}

	Date(const Date& d)
	{
		cout << "这里调用了拷贝构造" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	bool operator==(const Date& d)
	{
		return _day == d._day
			&& _year == d._year
			&& _month == d._month;
	}

//private:
	//给缺省值
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

bool operator==(const Date& d1, const Date& d2)
{
	return d1._day == d2._day
		&& d1._year == d2._year
		&& d1._month == d2._month;
}

Date func()
{
	Date d;
	return d;
}

int main()
{
	Date d1(20, 1, 3);
	Date d2(20, 1, 4);
	func();
	
	return 0;
}

        这里注意我们调用了func函数,返回的是Date类,会去调用类中的拷贝构造!但是可以发现,执行结果并没有执行拷贝构造中的语句,这是为啥?

        但是确实会调用拷贝构造,是因为优化没有调用。

        编译器在某些情况下会对代码进行优化,以提高执行效率。其中之一就是返回值优化(Return Value Optimization,RVO)。RVO 是一种优化技术,它可以避免不必要的拷贝构造函数调用。
        在 func() 函数中,返回了一个局部对象 d。如果编译器启用了 RVO,它会直接将 d 放在函数调用者的位置,而不是创建临时副本。这样,拷贝构造函数就不会被调用。

        但是我们严谨一点,这只是编译器帮助了我们优化,此时我们如何才能让其不拷贝?就是返回引用。

Date& func()
{
	Date d;
	return d;
}

        传值返回是返回d的拷贝,引用返回是d的别名。

class Date 
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		this->_year = year;
		this->_month = month;
		this->_day = day;
	}

	Date(const Date& d)
	{
		cout << "这里调用了拷贝构造" << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	bool operator==(const Date& d)
	{
		return _day == d._day
			&& _year == d._year
			&& _month == d._month;
	}

	void Print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}

	~Date()
	{
		cout << "调用了析构" << endl;
		_year = -1;
	}
//private:
	//给缺省值
	int _year = 1;
	int _month = 1;
	int _day = 1;
};

bool operator==(const Date& d1, const Date& d2)
{
	return d1._day == d2._day
		&& d1._year == d2._year
		&& d1._month == d2._month;
}

Date& func()
{
	Date d;
	return d;
}

int main()
{
	Date& d3 = func();
	d3.Print();
	
	return 0;
}

        如果不用引用返回,返回的是d的拷贝,这里d有是临时拷贝,临时对象具有常性。 当然,d3接收以后就可以修改它的属性。

Date& func()
{
	Date d(1022, 1, 3);
	return d;
}

int main()
{
	Date& d3 = func();
	d3.Print();
	return 0;
}

        再来观察以下代码:

Date& func()
{
	Date d(1022, 1, 3);
	return d;
}

int funcs()
{
	int a = 1;
	int b = 2;
	int c = 3;
	return a + b + c;
}

int main()
{
	Date& d3 = func();
	d3.Print();
	funcs();
	return 0;
}

        此时我们在调用一个函数,发现d3里面的值也已经改变,这是因为:

小结: 

        返回对象生命周期到了,会析构,传值返回。

        返回对象生命周期没到,不会析构,传引用返回。

        出了作用域,返回对象还没有析构,就可以用引用返回,减少拷贝。

         为了优化程序,我们一般对于类的返回使用的是引用返回。

        所以最好一般最好返回值,不要返回引用。

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

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

相关文章

【Keil程序大小】Keil编译结果Code-RO-RW-ZI分析

【Keil程序大小】Keil编译结果Code-RO-RW-ZI分析 下图为keil编译后的结果&#xff1a; 单位为Byte。Code是程序大小。RO是常量大小。RW是读写变量占用大小&#xff0c;如已初始化的静态变量和全局变量。ZI是全零变量占用大小&#xff0c;如未初始化的static修饰的静态变量、全局…

ENG-2 AM,129423-53-6主要用于检测生物体系中的Na+浓度

引言&#xff1a;在化学研究的海洋中&#xff0c;优质的化学试剂是实验成功的关键。今天&#xff0c;我要为大家分享一款备受好评的化学试剂——ENG-2。这款试剂以其独特的性能和广泛的应用领域&#xff0c;赢得了众多科研人员的青睐。 中文名称&#xff1a;钠离子荧光探针ENG-…

Linux进程——Linux进程间切换与命令行参数

前言&#xff1a;在上一篇了解完进程状态后&#xff0c;我们简单了解了进程优先级&#xff0c;然后遗留了一点内容&#xff0c;本篇我们就来研究进程间的切换&#xff0c;来理解上篇提到的并发。如果对进程优先级还有没理解的地方可以先阅读&#xff1a; Linux进程优先级 本篇…

无意的一次学习,竟让我摆脱了Android控制?

由于鸿蒙的爆火&#xff0c;为了赶上时代先锋。到目前为止也研究过很长一段时间。作为一名Android的研发人员&#xff0c;免不了对其评头论足&#xff0c;指导文档如何写才算专业&#xff1f;页面如何绘制&#xff1f;页面如何跳转&#xff1f;有没有四大组件等等。 而Harmony…

【数字经济】上市公司供应链数字化数据(2000-2022)

数据来源&#xff1a; 时间跨度&#xff1a;2000-2022年 数据范围&#xff1a;各上市企业 数据指标&#xff1a; 样例数据&#xff1a; 参考文献&#xff1a;[1]刘海建,胡化广,张树山,等.供应链数字化的绿色创新效应[J].财经研究,2023,49(03):4-18. 下载链接&#xff1a;https:…

neo4j-5.11.0安装APOC插件or配置允许使用过程的权限

在已经安装好neo4j和jdk的情况下安装apoc组件&#xff0c;之前使用neo4j-community-4.4.30&#xff0c;可以找到配置apoc-4.4.0.22-all.jar&#xff0c;但是高版本neo4j对应没有apoc-X.X.X-all.jar。解决如下所示&#xff1a; 1.安装好JDK与neo4j 已经安装对应版本的JDK 17.0…

Java快速入门系列-11(项目实战与最佳实践)

第十一章&#xff1a;项目实战与最佳实践 11.1 项目规划与需求分析项目规划需求分析实例代码 11.2 系统设计考虑实例代码 11.3 代码实现与重构实例代码 11.4 性能优化与监控实例代码 11.5 部署与持续集成/持续部署(CI/CD)实例代码 11.1 项目规划与需求分析 在进行任何软件开发…

Google Earth Engine谷歌地球引擎计算遥感影像在每个8天间隔内的多年平均值

本文介绍在谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#xff09;中&#xff0c;求取多年时间中&#xff0c;遥感影像在每1个8天时间间隔内的多年平均值的方法。 本文是谷歌地球引擎&#xff08;Google Earth Engine&#xff0c;GEE&#xff09;系列教学文章…

全双工音频对讲模块-支持空中升级、多级无线中继

SA618F30是一款高集成的大功率全双工无线音频模块&#xff0c;发射功率高达32dBm。该音频模块简化接口&#xff0c;只需外接音频功放或麦克风即可作为一个小型对讲机&#xff0c;方便快捷嵌入到各类手持设备中。支持多级无线中继&#xff0c;支持OTA空中升级。 SA618F30配备1W…

【前端】实现表格简单操作

简言 表格合并基础篇 本篇是在上一章的基础上实现&#xff0c;实现了的功能有添加行、删除行、逆向选区、取消合并功能。 功能实现 添加行 添加行分为在上面添加和在下面追加行。 利用 insertAdjacentElement 方法实现&#xff0c;该方法可以实现从前插入元素和从后插入元…

嘉楠堪智 CanMV K230 进行 Linux、RT-smart 系统开发

本文记录学习、使用 K230 SDK 进行 Linux、RT-smart 系统的开发的一些关键步骤&#xff0c;如何获取系统源代码&#xff0c;如何配置环境&#xff0c;如何使用 Docker 进行编译&#xff0c;获得系统文件。 具体详细的教程&#xff0c;可以学习 CanMV K230 教程。 目录 一、S…

引入 Redis

简介 Jedis Jedis 是早期的 Redis 的 Java 实现客户端&#xff0c;提供了比较全面的 Redis 命令的支持&#xff0c;其官方网址是&#xff1a;http://tool.oschina.net/uploads/apidocs/redis/clients/jedis/Jedis.html 优点&#xff1a;支持全面的 Redis 操作特性&#xff0…

搜狗输入法 PC端 v14.4.0.9307 去广告绿化版.

软件介绍 搜狗拼音输入法作为众多用户计算机配置的必备工具&#xff0c;其功能的全面性已为众所周知&#xff0c;并且以其高效便捷的输入体验受到广大使用者的青睐。然而&#xff0c;该软件在提供便利的同时&#xff0c;其内置的广告元素常常为用户带来一定的干扰。为此&#…

代码随想录算法训练营DAY48|C++动态规划Part9|121.买卖股票的最佳时机、122.买卖股票的最佳时机II、123.买卖股票的最佳时机III

文章目录 121.买卖股票的最佳时机思路CPP代码 122.买卖股票的最佳时机II思路CPP代码 123.买卖股票的最佳时机III思路CPP代码 121.买卖股票的最佳时机 力扣题目链接 文章讲解&#xff1a;121.买卖股票的最佳时机 视频讲解&#xff1a;动态规划之 LeetCode&#xff1a;121.买卖股…

由树形解空间入手,深入分析回溯、动态规划、分治算法的共同点和不同点

一、回溯、动态规划、分治 其实这三个算法就可以直接认为是一类算法&#xff0c;它们的共同点都是会涉及到递归。 更需要清楚明白的一点是&#xff0c;它们的解&#xff0c;都是基于一颗递归形式的树上得来的&#xff01;也就是——回溯、动态规划、分治的解空间是 一棵树&am…

ssm+vue的私人健身和教练预约管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的私人健身和教练预约管理系统(有报告)。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#xff0c;通…

深度学习常用优化算法笔记介绍,各种梯度下降法详细介绍

优化算法 mini-batch梯度下降法 当一个数据集其数据量非常大的时候&#xff0c;比如上百万上千万的数据集&#xff0c;如果采用普通的梯度下降法&#xff0c;那么运算速度会非常慢&#xff0c;因为如果使用梯度下降法在每一次迭代的时候&#xff0c;都需要将这整个上百万的数…

机器学习:基于TF-IDF算法、决策树,使用NLTK库对亚马逊美食评论进行情绪分析

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…

第一天复习Qt文件读取

Qt文件操作&#xff1a; 1、QFile QTextStream操作文件案例&#xff1a; 1、打开文件 QFile file(absolute filepath | relative path); file.readLine()返回内容长度&#xff0c;如果为-1就是读取失败 file. Close()读取后关闭 file.errorString()返回文件打开发生的错误2、…

数据仓库实验三:分类规则挖掘实验

目录 一、实验目的二、实验内容和要求三、实验步骤1、创建数据库和表2、决策树分类规则挖掘&#xff08;1&#xff09;新建一个 Analysis Services 项目 jueceshu&#xff08;2&#xff09;建立数据源视图&#xff08;3&#xff09;建立挖掘结构 DST.dmm&#xff08;4&#xff…