C++ ---- 类和对象(中)

news2025/2/26 5:01:09

目录

类的默认成员函数介绍

构造函数

构造函数概念

构造函数特性

析构函数

析构函数概念

析构函数特性

拷贝构造

拷贝构造概念

拷贝构造特点

赋值重载

赋值重载介绍

赋值重载特性

取地址重载和const取地址重载

const成员

 取地址和const取地址重载


类的默认成员函数介绍

当我们定义一个空类时,类中并非什么都没有,编译器会自动生成6个成员函数,这6个用户不显示实现,编译器会自动生成的函数称为默认成员函数

//什么也没定义的空类
//用户没有显示定义,编译器自动生成6个默认成员函数
class zxy
{
	//构造函数

	//拷贝构造

	//赋值重载
	
	//析构函数

	//取地址重载

	//const 取地址重载
};

●构造函数:主要完成初始化工作。

●析构函数:对资源进行清理。

●拷贝构造函数:用同类型对象创建对象。

●赋值重载:把一个对象赋值给另一个对象。

●取地址重载:对普通对象取地址。

●const取地址重载:对const对象取地址。

构造函数

构造函数概念

构造函数是特殊的成员函数,创建类类型对象时由编译器自动调用,在该对象生命周期内只调用一次!---- 构造函数的任务并不是开空间创建对象,而是初始化对象

对比以往的初始化操作(以日期为例):

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;
	d1.Init(2001,10,3);

	return 0;
}

观察上述代码,Date类型的对象可以通过Init公有函数设置日期完成初始化,也就说每次创建对象都要调用Init方法。对比调用Init初始化的方式,构造函数的出现就是懒人的福音,它会在对象创建时自动调用,完成初始工作。下面会详细介绍构造函数的用法及特性!

构造函数特性

●函数名和类名相同,没有返回值。

●创建对象时,编译器自动调用构造函数。

●构造函数可以重载。

●不用传参就能调用的构造,叫做默认构造。如:无参构造,全缺省构造。

●若未显示定义构造函数,编译器会默认生成。显示定义,编译器则不在生成。

●默认生成的构造函数对内置类型(int/double/int*...)不做处理,对自定义类型调用其自身的默认构造。

●针对第7条,默认生成的构造函数不对内置类型做处理的情况,C++11中,可以将内置类型在类中声明时给缺省值,如:class zxy{int a = 10;}; 注意这不是初始化,而是给a一个缺省值。

●自定义类型作为其他类的成员变量时,要提供默认构造。

1.函数名和类名相同,没有返回值!

class Date
{
public:
	//构造函数名和类名相同
	Date()
	{}
private:
	int _year;
	int _month;
	int _day;
};

3.创建类类型对象时,编译器自动调用对应的构造函数。

class Date
{
public:
	//构造函数名和类名相同
	Date()
	{
		cout << "Date():调用构造函数" << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

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

上图示例中并没有调用任何函数,只是创建了一个Date类型的对象d1,通过测试结果可以得知,当用自定义类型创建对象时,编译器会自动调用它的构造函数

4.构造函数可以重载。

根据不同的需求可以提供多个构造函数,无参,带参,全缺省,半缺省等。编译器会自动匹配合适的构造函数调用。

class Date
{
public:
	//无参
	Date()
	{
		//...
	}
	//带参
    Date(int year,int month,int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1;
	Date d2(2001, 10, 3);
	return 0;
}

需要注意的是,当构造函数不需要传参的时候,不要在对象名的后面加(),会和函数声明混淆。

错误的写法:
Date d1();

还要注意的是,如果同时提供了无参和全缺省,可能存在调用不明确的问题:在下述代码中同时提供了无参构造和全缺省构造,创建对象:Date d1; 构造函数调用不明确!!

class Date
{
public:
	//无参
	Date()
	{
		//...
	}
	//带参
    Date(int year = 2001,int month = 10,int day = 3)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

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

5.若未显示定义构造函数,编译器会默认生成。显示定义,编译器则不在生成。

6.不用传参就能调用的构造,叫做默认构造。如:无参构造,全缺省构造,默认生成的构造。

7.默认生成的构造函数对内置类型(int/double/int*...)不做处理,对自定义类型调用其自身的默认构造。

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
	//没有显示定义构造,默认生成
private:
	//内置类型
	int _year;
	int _month;
	int _day;
	//自定义类型
	Time _t;
};

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

8.针对第7条,默认生成的构造函数不对内置类型做处理的情况,C++11中,可以将内置类型在类中声明时给缺省值,如:int a = 10; 注意这不是初始化,而是给a一个缺省值。

class Date
{
	//没有显示定义构造,默认生成
private:
	//内置类型
	int _year = 1;
	int _month = 1;
	int _day = 1;
	//自定义类型
	Time _t;
};

 9.自定义类型作为其他类的成员变量时,要提供默认构造。

class Time
{
public:
	Time(int hour)
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};

将Time类中的构造函数定义成上述代码中的样子,此时显示定义了构造函数,编译器不会默认生成。我们定义的又不是默认构造(默认构造的几种回看第6条)。报错:_t不具备合适的默认构造

析构函数

析构函数概念

析构函数的功能和构造函数相反。析构函数会在对象销毁时自动调用(对象销毁的工作是编译器完成的,如函数中的局部对象,出了函数作用域就销毁),完成对象中资源的清理工作!

析构函数特性

●析构函数名在类名前加上字符~,没有参数,没有返回值类型。

●一个类只能有一个析构函数,未显示定义会默认生成,显示定义则不在生成!析构函数不能重载。

●对象生命周期结束时,编译器自动调用析构函数。

●编译器自动生成的析构函数,会对自定义类型成员调用它的析构函数。

●内置类型成员,销毁时不用资源清理,最后由系统将内存回收即可。

●如果类中没有申请资源时,析构函数可以不写,如:Date类。当涉及资源的申请时,要显示的定义析构函数,避免内存泄露,如statck,queue。

●析构的顺序:先创建的对象后销毁,后创建的对象先销毁。

1.析构函数名在类名前加上字符~。没有参数,没有返回值类型。

~Stack(){}

2.一个类只能有一个析构函数,未显示定义会默认生成,显示定义则不在生成!析构函数不能重载

5.对象生命周期结束时,编译器自动调用析构函数。(以Stack为例)

class Stack
{
public:
	//构造函数
	Stack(size_t capacity = 10)
	{
		_array = (int*)malloc(sizeof(int) * capacity);
		if (_array == nullptr)
		{
			perror("malloc申请空间失败!!");
			exit(1);
		}
		_capacity = capacity;
		_size = 0;

		cout << "Stack()构造" << endl;
	}
	//析构函数
	~Stack()
	{
		free(_array);
		_size = 0;
		_capacity = 0;

		cout << "~stack()析构" << endl;
	}
private:
	int* _array;
	size_t _size;
	size_t _capacity;
};

int main()
{
	Stack s1;

	return 0;
}

6.编译器自动生成的析构函数,会对自定义类型成员调用它的析构函数。内置类型成员,销毁时不用资源清理,最后由系统将内存回收即可。

class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (int*)malloc(sizeof(int)*capacity);
		if (_array == nullptr)
		{
			perror("malloc申请空间失败!!");
			exit(1);
		}
		_capacity = capacity;
		_size = 0;

		cout << "Stack()构造" << endl;
	}

	//析构,涉及资源清理,显示定义析构
	//析构的顺序和构造的顺序相反
	~Stack()
	{
		free(_array);
		_size = 0;
		_capacity = 0;

		cout << "~stack()析构" << endl;
	}
private:
	int* _array;
	size_t _size;
	size_t _capacity;
};

class MyQueue
{
	//没有显示定义构造函数,自定义类型调用其自身的默认构造

	//析构函数没有显示的定义,自定义类型调用其自身的析构函数
private:
	Stack s1;
	Stack s2;
};

int main()
{
	MyQueue my1;
	return 0;
}

 8.如果类中没有申请资源时,析构函数可以不写,如:Date类。当涉及资源的申请时,要显示的定义析构函数,避免内存泄露,如statck,queue。

拷贝构造

拷贝构造概念

用已存在的类类型对象创建新对象时,编译器自动调用拷贝构造函数。如:传值调用,传值返回。

拷贝构造特点

●拷贝构造是构造函数的重载。

●拷贝构造的参数必须为类类型的引用,且只有一个形参。

●拷贝构造如果使用传值的方式编译器会直接报错,因为会引发无穷递归,传值调用要发生拷贝,自定义类型对象调用自身的拷贝构造。

●拷贝构造若未显示定义,编译器会默认生成,默认生成的拷贝构造对内置类型按照字节拷贝,自定义类型去调用其自身的拷贝构造。(浅拷贝)

●在涉及资源的申请和释放场景中,拷贝构造需要显示的定义(要写析构,就要写拷贝构造)

1.拷贝构造是构造函数的重载。

拷贝构造函数名和类型相同,也没有返回值,参数为必须为类类型的引用,且只有一个形参,是构造函数的重载。

例:Stack (const Stack& st){}

加const的原因是为了防止在拷贝的过程中,将拷贝和被拷贝写反:如_A = st._A 写成 st._A = _A。

2.函数参数为类类型对象,形参是实参的一份临时拷贝,自动调用拷贝构造。

class Stack
{
public:
	//无参默认构造
	Stack()
	{
	}
	//拷贝构造
	Stack(const Stack& st)
	{
		cout << "拷贝构造调用" << endl;
	}
};

void Fun(Stack st)
{
	//...
}
int main()
{
	Stack st;
	Fun(st);
	return 0;
}

如上述代码,只是调用了Fun函数,该函数的参数类型是Stack类型的,在传值传参的过程中,发生了拷贝,自动调用该类类型的拷贝构造。

2.如果使用传值的方式编译器会直接报错,因为会引发无穷递归,传值调用要发生拷贝,自定义类型对象调用自身的拷贝构造,调用拷贝构造本身就要传参。所以拷贝构造的参数是类类型的引用

 4.拷贝构造若未显示定义,编译器会默认生成,默认生成的拷贝构造对内置类型按照字节拷贝,自定义类型去调用其自身的拷贝构造。

class B
{
public:
	~B()
	{
		cout << "~B():调用析构" << endl;
	}
private:
	int _b;
};

class A
{
private:
	int _a1;
	int _a2;
	B _b1;
};

int main()
{
	A a;
	return 0;
}

A类没有显示的定义拷贝构造,编译器自动生成,自定义类型调用其自己的拷贝构造,内置类型逐自己拷贝。

 为了避免干扰,只打印了自定义类型调用析构的效果。

浅拷贝问题:

class Stack
{
public:
	//构造
	Stack(size_t capacity = 10)
	{
		_array = (int*)malloc(sizeof(int)*capacity);
		_size = 0;
		_capacity = capacity;
	}

	//没有显示定义拷贝构造,编译器默认生成
	//内置类型按字节拷贝
	
	//析构
	~Stack()
	{
		free(_array);
		_size = _capacity = 0;
	}
private:
	int* _array;
	size_t _size;
	size_t _capacity;
};

int main()
{
	Stack st1;
	Stack st2(st1);
	return 0;
}

 解决:显示的定义拷贝构造,改用深拷贝

	Stack(const Stack& st)
	{
		_array = (int*)malloc(sizeof(int)*st._capacity);
		memcpy(_array,st._array,sizeof(int)*st._capacity);

		_size = st._size;
		_capacity = st._capacity;

		cout << "深拷贝" << endl;
	}

 如上图所示,st2对st1进行了深拷贝,各自有独立的空间,插入数据互相也不会影响。

5.在涉及资源的申请和释放场景中,拷贝构造需要显示的定义(要写析构,就要写拷贝构造)。

6.拷贝构造的调用场景:

  • 使用已经存在的对象创建新对象。
class A
{
public:
	A() {}
	A(const A& a)
	{
		cout << "拷贝构造调用" << endl;
	}
};

int main()
{
	A a1;
	A a2(a1);
	return 0;
}

  • 函数参数为类类型对象,形参是实参的一份临时拷贝,自动调用拷贝构造。
void Fun(A a)
{
	//...
}

int main()
{
	A a1;
	Fun(a1);
	return 0;
}

  • 当函数返回值为类类型对象,传值返回的过程中要产生临时变量,先存在寄存器或者上层栈帧中,这个期间的拷贝调用拷贝构造。
A Fun()
{
	A aa;
	return aa;
}

int main()
{
	A a1;
	Fun();
	return 0;
}

赋值重载

赋值重载介绍

赋值重载的主要功能就是将一个对象赋值给另一个对象,注意和拷贝构造进行区分,拷贝构造是用一个已经存在的对象创建一个新对象,赋值重载是将一个对象赋值给另一已经存在的对象。谈到拷贝和赋值,有一个容易混淆的写法:

	A a1;
	A a2 = a1;
class A
{
public:
	A() {}
	A(const A& a)
	{
		cout << "拷贝构造" << endl;
	}
	A& operator=(const A& a)
	{
		cout << "赋值重载" << endl;
	}
};

int main()
{
	A a1;
	A a2 = a1;
	return 0;
}

 上述的写法实际上调用的是拷贝构造,本质上是用一个存在的对象创建另一个对象。

 接着回到正文,为了增加代码的可读性,C++引入了运算符重载,赋值重载就是运算符重载中的一种。运算符重载具有特殊的函数名(关键字operator关键字后面接需要重载的运算符号如:+、-)。

返回类型 operator操作符(){}

因为赋值重载是运算符重载的一种,所以先来谈论什么是运算符重载:以日期类为例,自定义类型的两个日期类对象,直接使用==、+、-这些运算符是不可以的:

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

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

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

int main()
{
	Date d1(2001,10,3);
	Date d2(2001, 2, 9);

	d1 += d2;
	d1 -= d2;

	return 0;
}

 基于上述问题,我们可以在类中定义一个成员函数来判断两个日期的大小,或者实现其它功能

bool AddFun()
{
   //...日期加法的逻辑
}

在我们有需求时,调用成员函数即可,如:d1.AddFun(d2);  这样的写法和我们熟知的+、-、==、>等运算符比较,可读性更差一些!所以引入了运算符重载,在使用上和正常的+、-操作没什么不同,下面对部分运算符进行重载,观察运行效果:

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

	void Show()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
	//重载==
	bool operator==(const Date& d1)
	{
		cout << "调用重载的==" << endl;

		return _year == d1._year
			&& _month == d1._month
			&& _day == d1._day;
	}
	//重载!=
	bool operator!=(const Date& d1)
	{
		return !(*this == d1);
	}
	//重载>
	bool operator>(const Date& d1)
	{
		cout << "调用重载的>" << endl;
		if (_year > d1._year)
		{
			return true;
		}
		else if(_year == d1._year && _month > d1._month)
		{
			return true;
		}
		else if (_year == d1._year && _month == d1._month && _day > d1._day)
		{
			return true;
		}
		return false;
	}

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

int main()
{
	Date d1(2001,10,3);
	Date d2(2001, 2, 9);

	//d1>d2 等价 d1.operator>(d2)
	cout << (d1 > d2) << endl;
	//d1==d2 等价 d2.operator==(d2)
	cout << (d1 == d2) << endl;
	return 0;
}

运算符重载后,就可以按照正常的使用习惯进行操作,例如:d1 == d2,d1 > d2。但是它们的原型是d1.operator==(d2); d1.operator>(d2)。 运算符重载后,在使用上提供了便捷,在代码的可读性上更为直观。

关于运算符重载还有几点要谈:

1.不能通过operator关键字连接其他符号创建新的操作符,如:operator@。

2.重载操作符必须有一个类类型参数,也就是说不能随便重载,比如:int + int

int operator+(int a, int b)
{
	return a * 10 + b;
}

int main()
{
	int a = 10,b =5;
	int c = a + b;
	return 0;
}

3,作为类成员函数时,形参比操作数少1,因为成员函数的第一个参数为隐藏的this指针。

4.    .*     ::      sizeof    ?:     . 注意以上5个运算符不能重载。

5.前置++/--和后置++/--。(相关细节参考日期类模拟实现的文章)。

对于内置类型而言,后置++/--比前置++/--多了一次拷贝,在实现上后置的++/--的参数列表要显示的写int,这是和编译器的暗号,用来区分是前置++/--还是后置++/--。

T& operator++()
{
    //...
}

T operator++(int)
{   
    //...
}

还需要说明的是,前置++适合返回类类型的引用,减少一次拷贝。而后置++返回的是局部变量,只能传值返回。

6.流插入和流提取运算符重载。(相关细节参考日期类模拟实现的文章)。

流插入和流提取在基础篇介绍过,它们分别是istream和ostream的对象,对于自定义类型而言,流插入和流提取是无法识别的,因为库中只实现了内置类型的<<和>>重载。当我们想要自定义类型的对象像内置类型一样输出和输出时,需要重载<<和>>。

赋值重载特性

●赋值运算符重载:返回值类型  operator=(const T& )。

●参数类型const T& ,减少拷贝。返回值类型T&,支持连续赋值,d1 = d2 = d3。

●返回值返回*this,支持连续赋值。

●自己给自己赋值的情况要检查,如:d1 = d2。

●不显示定义编译器会自动生成,内置类型逐字节拷贝,自定义类型调用其自己的赋值重载。

●赋值运算符重载只能重载成类的成员函数,不能重载成全局函数。

●当有资源申请,没有显示定义赋值重载的情况,可能会出现内存泄露和重复析构。

1、赋值运算符重载单参数为const T& ,返回值为T&,支持连续赋值。(以Stack为例:)

class Stack
{
public:
	//赋值重载
	Stack& operator=(const Stack& s1)
	{
		//....
		return *this;
	}
private:
	int* _array;
	int _size;
	int _capacity;
}

2.不显示定义编译器会自动生成,内置类型逐字节拷贝,自定义类型调用其自己的赋值重载。

class A
{
public:
	A& operator=(const A& A)
	{
		_a1 = A._a1;
		_a2 = A._a2;
		cout << "A& operator=(const A& A)" << endl;
		return *this;
	}
private:
	int _a1=10;
	int _a2=20;
};

class B
{
	//没有显示的定义赋值重载,内置类型按字节拷贝,自定义类型调用其对应的
	//赋值重载
private:
	A a;
	int b;
};

int main()
{
	A a1;
	A a2;

	a2 = a1;
	return 0;
}

B类没有显示定义赋值重载,默认生成的赋值重载函数对于自定义类型会调用其自己的赋值重载,内置类型按照字节拷贝。

3.定义赋值重载函数,无论形参的类型是什么,赋值运算符重载都要定义为成员函数!!!

原因:赋值运算符在类中不显示实现,会默认的生成一个。此时如果在类外定义了一个全局的赋值重载函数,两个函数就会冲突。

class A
{
	//没有显示定义赋值重载,编译器自动生成一个
private:
	int _a;
	int _a2;
};

//定义全局的赋值重载函数
A& operator=(const A& left, const A& right)
{
	//....
}

int main()
{
	A a1;
	A a2;
	a1 = a2;
	return 0;
}

 需要注意的是,全局函数的参数没有this指针,所以需要传两个参数。

对于其他运算符重载,可以定义为全局函数,在传参的时候注意要传两个参数:

class A
{
public:
	A(int a1, int a2)
	{
		_a1 = a1;
		_a2 = a2;
	}
public:
	int _a1;
	int _a2;
};

//定义全局的赋值重载函数
bool operator==(const A& left, const A& right)
{
	cout << "全局运算符重载==" << endl;
	return left._a1 == right._a1
		|| left._a2 == right._a2;
}

int main()
{
	A a1(10,20);
	A a2(20,30);

	cout << (a1 == a2) << endl;
	return 0;
}

(a1 == a2) 等价于 operator=(a1,a2) ;

但是一般情况下,将运算符重载定义为成员函数更为常见,这是因为,类的成员变量一般都会设为私有,运算符重载经常要访问成员变量。上述代码为了演示将成员变量设为了公有。

4.当有资源申请,没有显示定义赋值重载的情况,可能会出现内存泄露和重复析构。(以Stack为例):

class Stack
{
public:
	//构造函数
	Stack(size_t capacity = 10)
	{
		_array = (int*)malloc(sizeof(int)*capacity);
		_size = 0;
		_capacity = capacity;
	}
	//析构,有资源的申请,显示定义析构
	~Stack()
	{
		free(_array);
		_array = nullptr;
		_size = _capacity = 0;
	}

	//拷贝构造,有资源的申请,显示定义,深拷贝
	//用已有的对象构建新对象
	Stack(const Stack& st)
	{
		_array = (int*)malloc(st._capacity*sizeof(int));
		memcpy(_array, st._array, sizeof(int) * st._size);

		_size = st._size;
		_capacity = st._capacity;
	}
    
    //赋值重载没有显示定义,用默认生成的
	//赋值重载,将已经存在的对象赋值给另一个存在的对象
	//涉及资源的申请,如果用默认生成的赋值,会造成资源泄露和多次析构的问题


	void Push(const int val)
	{
		//简单模拟,不考虑扩容问题
		_array[_size++] = val;
	}
private:
	int* _array;
	int _size;
	int _capacity;
};

 

如上图分析,当涉及资源的申请时,要显示的定义合适的赋值重载:

	Stack& operator=(const Stack& st)
	{
		//自己赋值自己直接返回即可
		if (this != &st)
		{
			//先把旧的空间释放,再去拷贝
			free(_array);

			_array = (int*)calloc(st._capacity,sizeof(int));
			if (_array == nullptr)
			{
				perror("malloc失败!");
				exit(1);
			}
			memcpy(_array, st._array, sizeof(int) * st._size);
			_size = st._size;
			_capacity = st._capacity;
		}
		return *this;
	}

在上述代码中,还有一个细节,就是对自己赋值给自己的情况直接略过不进行处理,如果忽略了这种情况,可能会出现bug,以上述代码为例:

综上所述,在赋值重载的实现中,要注意自己给自己赋值的情况。 

取地址重载和const取地址重载

const成员

在类和对象上篇谈论过,隐含的this指针类型是T* const this。 const 修饰的是指针本身,禁止this指针改变指向。基于这种情况,如果我们有const对象调用成员函数的时候,const对象调用非const成员函数,是权限的放大,这样的调用是错误的。

class Date
{
public:
	void Fun()
	{
		//...
	}
private:
	int _year;
	int _month;
	int _day;
};

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

上述的调用是const对象调用非const的成员函数,是权限的放大,不可以。下述代码是正确的写法:

class Date
{
public:
	Date() {}
	void Fun() const
	{
		//...
	}
private:
	int _year = 1;
	int _month = 2;
	int _day = 3;
};

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

在函数后面加const修饰,const修饰的是this指针指向内容,表示在该成员函数中不能对类的成员变量进行修改。

 取地址和const取地址重载

这两个默认函数不写,编译器会默认生成。通常情况下,编译器默认生成的就足够满足我们的需求,它们的功能分别是对普通对象取地址和对const对象取地址。

class Date
{
public:
	Date* operator&()
	{
		return this;
	}
	const Date* operator&()const
	{
		return this;
	}
private:
	int _year = 1;
	int _month = 2;
	int _day = 3;
};

int main()
{
	const Date d1;
	Date d2;

	cout << &d1 << endl;
	cout << &d2 << endl;
	return 0;
}

 这两个默认成员函数一般不用显示的写,编译器默认生成的就足够用了。

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

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

相关文章

【致敬未来的攻城狮计划】— 连续打卡第三十天:总结与回顾

学习目标&#xff1a; 自2023年4月13日开始&#xff0c;我参加了为期一个月的【致敬未来的攻城狮计划】&#xff0c;今天是第三十天&#xff0c;做一个总结和回顾。 我参加的是【致敬未来的攻城狮计划】第二期&#xff08;攻城狮计划&#xff09; 在这里首先还是感谢 李…

【云服务器】关于UDP/TCP跨平台网络通信服务器无响应的情况及解决办法

关于跨平台网络通信服务器无反应的情况 一、问题出现二、云服务器Centos7防火墙开放端口2.1 检查防火墙状态2.2 开启防火墙2.3 在running 状态下&#xff0c;向firewall 添加需要开放的端口2.4 重新加载防火墙配置2.5 查看端口是否放开 三、云服务器防火墙配置开放端口3.1 进入…

决策树与随机森林

决策树解决回归问题时进行平均数计算。 决策树 (1)熵&#xff08;entropy)与特征节点 熵&#xff08;entropy&#xff09;&#xff0c;度量着信息的不确定性&#xff0c;信息的不确定性越大&#xff0c;熵越大。信息熵和事件发生的概率成反比。 ■信息熵代表随机变量的复杂度…

c++《list容器的使用》

本文主要介绍list的一些常见接口的使用 文章目录 一、list的介绍二、list的使用2.1 list的构造函数2.2 list迭代器的使用2.3 list相关的容量大小相关的函数2.4 list数据的访问相关的函数2.5 list的数据调整相关的函数2.6 list中其他函数操作 一、list的介绍 list是可以以O(1)的…

IOC理论推导

1.UserDao接口 package com.kuang.dao;public interface UserDao {void getUser(); }2.UserDaoImpl业务接口 package com.kuang.dao;public class UserDaoImpl implements UserDao{Overridepublic void getUser() {System.out.println("默认获取用户数据");} }3.Us…

【ChatGPT】国内免费使用的ChatGPT镜像

Yan-英杰的主页 悟已往之不谏 知来者之可追 C程序员&#xff0c;2024届电子信息研究生 目录 什么是ChatGPT镜像&#xff1f; 亲测&#xff1a; 一、二狗问答(AI对话) 二、AiDuTu 三、WOChat 四、ChatGPT(个人感觉最好用) 我们可以利用ChatGPT干什么&#xff1f; 一、三分…

基于Python3的tkinter Text文本框加滚动条显示信息

用tkinter进行界面程序开发中&#xff0c;经常需要将信息展示到界面上&#xff0c;给用户及时的反馈和想要看到的结果。Text控件允许用户以不同的样式、属性来显示和编辑文本&#xff0c;它可以包含纯文本或者格式化文本&#xff0c;同时支持嵌入图片、显示超链接以及带有 CSS …

C++ Primer Plus——第6章 分支语句和逻辑运算符

第6章 分支语句和逻辑运算符 6.1 if语句6.1.1 if else语句6.1.2 格式化if else语句6.1.3 if else if else结构 6.2 逻辑表达式6.2.1 逻辑OR运算符&#xff1a;||6.2.2 逻辑AND运算符&#xff1a;&&6.2.3 用&&来设置取值范围 6.1 if语句 if语句让程序能够决定是…

CSP-202212-2 训练计划

目录 一、题目 二、思路 三、C代码如下 一、题目 问题背景 西西艾弗岛荒野求生大赛还有 n 天开幕&#xff01; 问题描述 为了在大赛中取得好成绩&#xff0c;顿顿准备在 n 天时间内完成“短跑”、“高中物理”以及“核裂变技术”等总共 m 项科目的加强训练。其中第 i 项…

【源码解析】@ControllerAdvice实现异常捕获与响应增强处理的原理解析

全局异常处理 demo展示 Slf4j RestControllerAdvice public class GlobalExceptionAdvice {ExceptionHandler(RuntimeException.class)public R<Void> handleNotPermissionException(RuntimeException e, HttpServletRequest request) {String requestURI request.get…

卷土重来?我羊羊羊羊羊了!

大家注意&#xff1a;因为微信最近又改了推送机制&#xff0c;经常有小伙伴说错过了之前被删的文章&#xff0c;比如前阵子冒着风险写的爬虫&#xff0c;再比如一些限时福利&#xff0c;错过了就是错过了。 所以建议大家加个星标&#xff0c;就能第一时间收到推送。&#x1f44…

顶尖作品集封面封底来了 共28套

各位打算换工作的都找到心仪东家没? 我们都知道作品集作为我们的敲门砖 其重要性不言而喻 如何让我们的作品集脱颖而出 这里作品集封面就很重要一个设计感超强的封面 可以让面试官眼前一亮 今天给大家整理了28款设计师专属作品集封面封底 让HR看后过目不忘大大提高面试…

删除二叉搜索树中的节点

1题目 给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key 对应的节点&#xff0c;并保证二叉搜索树的性质不变。返回二叉搜索树&#xff08;有可能被更新&#xff09;的根节点的引用。 一般来说&#xff0c;删除节点可分为两个步骤&#xff1a…

Oracle EBS Interface/API(48)- AP发票取消API

快速参考 参考点内容功能导航N: AP->发票->录入->发票并发请求None基表AP.AP_INVOICES_ALLAPI参考下面介绍错误信息表None接口FormNone接口RequestDebug ProfileNone详细例子参考如下实例官方文档None数据验证包None用户界面 Path:AP->发票>录入>发票->活…

Android Studio下配置NDK和Cmake

文章目录 NDK简介AS上安装NDK和CmakeAS项目中添加支持C和CMake NDK简介 ndk是SDK的扩展部分&#xff0c;ndk是一套在Android Studio 上支持开发及编译链接C/C的工具链。 AS上安装NDK和Cmake 点击完OK后&#xff0c;等待安装就行了。安装完毕后&#xff1a; 在local.propertie…

Windows在外远程桌面控制macOS【macOS自带VNC远程】

文章目录 前言1.测试局域网内远程控制1.1 macOS打开屏幕共享1.2 测试局域网内VNC远程控制 2. 测试公网远程控制2.1 macOS安装配置cpolar内网穿透2.2 创建tcp隧道&#xff0c;指向5900端口 3. 测试公网远程控制4. 配置公网固定TCP地址4.1 保留固定TCP地址4.2 配置固定TCP端口地址…

使用Process Monitor排查因dll库被锁定导致C++程序启动报“0xc0000022”错误问题

目录 1、问题描述 2、查找到异常值0xc0000022对应的标识STATUS_ACCESS_DENIED 3、使用Process Monitor定位到哪个dll库被隔离了 4、在360安全卫士中将被隔离的文件恢复了&#xff0c;但360并没有释放对文件的锁定 VC常用功能开发汇总&#xff08;专栏文章列表&#xff0c;欢…

5.12 C高级 作业

有m1.txt m2.txt m3.txt m4.txt&#xff0c;分别创建出对应的目录&#xff0c;m1 m2 m3 m4 并把文件移动到对应的目录下 #!/bin/bashvar1(ls *.txt)for i in ${var1[*]} doposexpr index $i .var2expr substr $i 1 $((pos-1))mkdir $var2mv $i $var2 done使用break关键字打印九…

【算法】【算法杂谈】将路径数组变为统计数组(单数组的调整与转换)

目录 前言问题介绍解决方案代码编写java语言版本c语言版本c语言版本 思考感悟写在最后 前言 当前所有算法都使用测试用例运行过&#xff0c;但是不保证100%的测试用例&#xff0c;如果存在问题务必联系批评指正~ 在此感谢左大神让我对算法有了新的感悟认识&#xff01; 问题介…

rocketMQ的架构原理和读写逻辑

NameServer 无状态节点&#xff0c;集群部署&#xff0c;节点之间无任何信息同步&#xff0c;支持横向拓展&#xff1b; producer & consumer也是无状态的&#xff0c;每一个producer之间 &#xff0c;每一个consumer之间都不会通信&#xff0c; 每个producer和consumer内部…