C++类与对象(二)

22 篇文章 0 订阅

类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类。
但是任何类里编译器都会自动生成六个默认函数,就跟this的隐藏属性相似,当然你也可以自己去定义,编译器就不会自动生成了。
默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
六个默认函数在类里面也被成为特殊函数成员。
在这里插入图片描述
也就是说,有些是需要我们写的,有些是不需要我们写。

构造函数

概念

比如之前用C语言写的栈,每次创建一个新的对象,都要进行初始化和销毁,不过有时候我们会忘记,所以在C++当中,就在类里面进行了初始化。
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。

特性

1.数名与类名相同。
2.无返回值。
3.对象实例化时编译器自动调用对应的构造函数。
4.构造函数可以重载。(提供多种初始化方式,这里也要注意无参和全缺省的二义性)

#include <iostream>
using namespace std;
class Date
{
public:
	Date (int year, int month, int day)//有参构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date()//无参构造函数
	{
		_year = 2022;
		_month = 10;
		_day = 11;
	}
	void print()
	{
		cout << _year << ' ' << _month << ' ' << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date s1(2022, 10, 10);//调用有参构造函数
	s1.print();
	Date s2;//调用无参构造函数,这里不能带括号,不然和函数声明冲突
	s2.print();
	return 0;
}

构造函数在你创建对象的时候就调用了,它是一个初始化的函数,并不是给对象开辟空间的函数。
在这里插入图片描述
5.如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦
用户显式定义编译器将不再生成。
当然这里自动生成也是非常有特点的:

C++把数据类型分为两个类型,内置类型(int 、char等等),自定义类型(class、struct等等)。
默认构造函数只会初始化自定义类型。

#include <iostream>
using namespace std;
class N1
{
public:
	N1()//不需要传参的是默认构造函数
	{
		_a = 10;
		cout << "N1" << endl;
	}
private:
	int _a;
};

class N2
{
public:
	void print()
	{
		cout << _x << ' ' << _y << endl;
	}
private:
	//内置类型
	int _x;
	int _y;
	//自定义类型
	N1 s;
};
int main()
{
	N2 p;
	p.print();
	return 0;
}

在这里插入图片描述
自定义的类型在初始化的时候,会调用该类的默认构造函数进行初始化。
如果这里不是默认构造函数就会报错。
在这里插入图片描述
注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。

#include <iostream>
using namespace std;
class N1
{
public:
	N1()//构造函数
	{
		_a = 10;
		cout << "N1" << endl;
	}
private:
	int _a;
};

class N2
{
public:
	void print()
	{
		cout << _x << ' ' << _y << endl;
	}
private:
	//内置类型
	int _x = 10;//缺省值
	int _y = 11;
	//自定义类型
	N1 s;
};
int main()
{
	N2 p;
	p.print();
	return 0;
}

在这里插入图片描述
如果内置类型有缺省值并且构造函数没有给内置类型初始化,那么就会用缺省值进行初始化。
缺省值的作用很大,你甚至可以利用缺省值在成员变量中的指针上开辟一块空间,但是无法检测开辟的这一块空间是否为空。
在这里插入图片描述

6.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

#include <iostream>
using namespace std;
class N1
{
public:
	N1()
	{
		_a = 10;
		_b = 5;
	}
	N1(int a = 2, int b = 4)
	{
		_a = a;
		_b = b;
	}
	void print()
	{
		cout << _a << ' ' << _b << endl;
	}
private:
	int _a;
	int _b;
};
int main()
{
	N1 s;
	s.print();
	return 0;
}

在这里插入图片描述
这里要注意全缺省的特性。

析构函数

概念

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

特性

1.析构函数名是在类名前加上字符 ~。
2.无参数无返回值类型。
3.一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构
函数不能重载。

4.对象生命周期结束时,C++编译系统系统自动调用析构函数。

#include <iostream>
using namespace std;
class stack
{
public:
	stack(int capacity = 4)
	{
		_a = (int*)malloc(capacity * sizeof(int));
		if (_a == nullptr)
		{
			perror("空间申请失败");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~stack()//析构函数
	{
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
	void Push(int x)//入栈
	{
		//扩容等等操作....
		_a[_top] = x;
		_top++;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	stack s1;
	s1.Push(1);
	s1.Push(2);
	return 0;
}

在这里插入图片描述
当对象所在的函数生命周期结束时就会调用析构函数。
这里就释放掉了之前开辟的内存。
5.编译器自动生成析构函数的特性

#include <iostream>
using namespace std;
class N1
{
public:
	N1()
	{
		_x = 1;
		_y = 2;
	}
	~N1()
	{
		_x = 0;
		_y = 0;
	}
private:
	int _x;
	int _y;
};
class N2
{
public:
	N2()
	{
		_a = 10;
	}
private:
	int _a;
	N1 _s;
};
int main()
{
	N2 s;
	return 0;
}

在这里插入图片描述
在这里插入图片描述

  1. 自动生成的析构函数会调用自定义类型的析构函数。
  2. 不会重置成员变量,比如让成员变量a=0这种操作是没有的,只会等待结束让他归还操作系统。
  3. 如果是开辟出来的空间也不会销毁(例如malloc和realloc)。

注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数

拷贝构造函数

概念

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存
在的类类型对象创建新对象时由编译器自动调用。

特性

拷贝构造函数典型调用场景:

使用已存在对象创建新对象
函数参数类型为类类型对象
函数返回值类型为类类型对象

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

#include <iostream>
using namespace std;
class N1
{
public:
	N1(int x, int y)
	{
		_x = x;
		_y = y;
	}
	N1(const N1& s)//拷贝函数
	{
		_x = s._x;
		_y = s._y;
	}
	void print()
	{
		cout << _x << ' ' << _y << endl;
	}
private:
	int _x;
	int _y;
};

int main()
{
	N1 s1(1, 2);
	N1 s2(s1);//这里是在s2即将创建的时候拷贝了s1的内容
	s1.print();
	s2.print();
	return 0;
}

在这里插入图片描述
只要是拷贝,被拷贝的对象就不会进入构造函数,而是进入拷贝函数。
拷贝构造的参数如果不用引用,就会无限递归,因为传参的时候是参与形参也是一次拷贝函数,无限的自己调用自己就会死循环。
在这里插入图片描述
首先s2传入this指针(红线),然后s1传如s中,这个过程也是两个自定义类型的对象进行拷贝构造,所以再次调用自己,以此类推,死循环。
3.若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按
字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

#include <iostream>
using namespace std;
class N1
{
public:
	N1(int x, int y)
	{
		_x = x;
		_y = y;
	}
	void print()
	{
		cout << _x << ' ' << _y << endl;
	}
private:
	int _x;
	int _y;
};

int main()
{
	N1 s1(1, 2);
	N1 s2(s1);
	s1.print();
	s2.print();
	return 0;
}

在这里插入图片描述
注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。
4.默认的拷贝函数无法进行深拷贝。
那么,如果我们成员变量里面有一处空间,默认的拷贝函数就无法进行拷贝。

#include <iostream>
using namespace std;
class stack
{
public:
	stack(int capacity = 4)
	{
		cout << "初始化" << endl;

		_a = (int*)malloc(capacity * sizeof(int));
		if (_a == nullptr)
		{
			perror("malloc开辟失败");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~stack()
	{
		cout << "析构" << endl;
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity;
	}
	void Push(int x)
	{
		//判断扩容条件.....
		cout << "入栈" << endl;
		_a[_top] = x;
		_top++;
	}
private:
	int* _a;
	int _capacity;
	int _top;
};

int main()
{
	stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	stack s2(s1);
	return 0;
}

这里我为了方便显示初始化了几次和析构了几次所以用了打印。
在这里插入图片描述

初始化就初始化了一次,因为第一次是创建一个对象需要初始化,第二次创建的对象是进行拷贝。
入栈都很正常,析构第一次也很正常,但是第二次析构这里就出问题了。
在这里插入图片描述
到现在对象s1还是正常的。
在这里插入图片描述
然后是拷贝,这里我们发现一个很严重的事情,就是s1,s2对象成员_a指向了同一个地方,那么在析构的时候,就会产生释放这块空间两次的问题。
所以这种情况下就需要深拷贝:
创建s2对象的时候,在拷贝函数中新开辟一个空间然后赋给s2成员变量_a,然后将s1中的数据以此拷贝到s2中。

#include <iostream>
using namespace std;
class stack
{
public:
	stack(int capacity = 4)
	{
		cout << "初始化" << endl;

		_a = (int*)malloc(capacity * sizeof(int));
		if (_a == nullptr)
		{
			perror("malloc开辟失败");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~stack()
	{
		cout << "析构" << endl;
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity;
	}
	stack(const stack& s)
	{
		_a = (int*)malloc(s._capacity * sizeof(int));
		if (_a == nullptr)
		{
			perror("malloc开辟失败");
			exit(-1);
		}
		_capacity = s._capacity;
		_top = s._top;
		memcpy(_a, s._a, sizeof(int) * s._top);
	}
	void Push(int x)
	{
		//判断扩容条件.....
		cout << "入栈" << endl;
		_a[_top] = x;
		_top++;
	}
private:
	int* _a;
	int _capacity;
	int _top;
};

int main()
{
	stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	stack s2(s1);
	return 0;
}

在这里插入图片描述
这次就没有报错了。
那么自定义类型呢?

#include <iostream>
using namespace std;
class stack
{
public:
	stack(int capacity = 4)
	{
		cout << "初始化" << endl;

		_a = (int*)malloc(capacity * sizeof(int));
		if (_a == nullptr)
		{
			perror("malloc开辟失败");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~stack()
	{
		cout << "析构" << endl;
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity;
	}
	stack(const stack& s)
	{
		cout << "拷贝" << endl;
		_a = (int*)malloc(s._capacity * sizeof(int));
		if (_a == nullptr)
		{
			perror("malloc开辟失败");
			exit(-1);
		}
		_capacity = s._capacity;
		_top = s._top;
		memcpy(_a, s._a, sizeof(int) * s._top);
	}
	void Push(int x)
	{
		//判断扩容条件.....
		cout << "入栈" << endl;
		_a[_top] = x;
		_top++;
	}
private:
	int* _a;
	int _capacity;
	int _top;
};
class N
{
private:
	//自定义类型
	stack N1;
	stack N2;
};
int main()
{
	N s1;
	N s2(s1);
	return 0;
}

在这里插入图片描述
在调用默认拷贝函数的时候,s1对象中的两个自定义成员是已经创建并且初始化的了,而s2实在创建中,所以就需要调用stack类的拷贝函数。

赋值运算函数

概念

如果你想对比同类的两个对象呢?
C++为了增强代码的可读性引入了运算符重载(为了让自定义类型能用运算符),运算符重载是具有特殊函数名的函数,也具有其
返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)。

注意:

  1. 不能通过连接其他符号来创建新的操作符:比如operator@。
  2. 重载操作符必须有一个类类型参数。
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义。
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this。
  5. .* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
#include <iostream>
using namespace std;
class Date
{
public:
	Date(int x, int y, int z)
	{
		_a = x;
		_b = y;
		_c = z;
	}
	//这里我就不设为私有了,不然暂时无法演示不在类中的效果
	int _a;
	int _b;
	int _c;
};
bool operator==(const Date& x1, const Date& x2)
{
	return x1._a == x2._a && 
		x1._b == x2._b && 
		x1._c == x2._c;
}

int main()
{
	Date s1(1, 2, 3);
	Date s2(1, 2, 3);
	cout << (s1 == s2) << endl;//这里的s1 == s2要加一个括号,不然<<的优先级高
	//s1 == s2 的方式也可以写成这样,不过一般不会,operator==(s1, s2)
	return 0;
}

在这里插入图片描述
那么如果将成员变量搞成私有呢,我们怎么办。(友元后面说,现在不用)
这时把他放在类里面就可以了。

#include <iostream>
using namespace std;
class Date
{
public:
	Date(int x, int y, int z)
	{
		_a = x;
		_b = y;
		_c = z;
	}
	bool operator==(const Date& x2)//这里要注意this指针的问题
	{
		return _a == x2._a &&
			_b == x2._b &&
			_c == x2._c;
	}
private:
	int _a;
	int _b;
	int _c;
};


int main()
{
	Date s1(1, 2, 3);
	Date s2(1, 2, 3);
	cout << (s1 == s2) << endl;//这里的s1 == s2就等于 s1.operator==(s2)
	return 0;
}

在这里插入图片描述

赋值运算符重载

这个和拷贝函数有点类似,只不过拷贝是被拷贝的是在创建的过程中被赋值同类对象的初始化,这个是两个已经创建的同类对象进行复制拷贝。
注意:如果我们不去定义,编译器会在类里面默认生成一个默认赋值运算符重载,以值的方式逐字节拷贝,所以这里不能全局函数,因为会与类里面的函数冲突。

#include <iostream>
using namespace std;
class N1
{
public:
	N1(int x, int y)
	{
		_a = x;
		_b = y;
	}
private:
	int _a;
	int _b;
};

int main()
{
	N1 s1(1, 2);
	N1 s2(3, 4);
	s1 = s2;
	return 0;
}

在这里插入图片描述
在这里插入图片描述
这里因为返回的是一个对象,所以想通过流打印出来还是很困难的。
1.赋值运算符重载格式
参数类型:const &,传递引用可以提高传参效率。
返回值类型:&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值检测是否自己给自己赋值。
返回*this :要复合连续赋值的含义。
注意区分左右。

#include <iostream>
using namespace std;
class N1
{
public:
	N1(int x, int y)
	{
		_a = x;
		_b = y;
	}
	//赋值运算
	N1& operator=(const N1& s)//这里也可以不用引用,但是效率会降低,因为传值传的是构造体的拷贝
	{
		_a = s._a;
		_b = s._b;
		return *this;//返回值是s1,一般返回的都是左值
	}
	void print()
	{
		cout << _a << ' ' << _b << endl;
	}
private:
	int _a;
	int _b;
};

int main()
{
	N1 s1(1, 2);
	N1 s2(3, 4);
	N1 s3(5, 6);
	s3 =s1 = s2;
	s3.print();
	return 0;
}

在这里插入图片描述
2.自定义类型的赋值
因为和拷贝极其相似,那么这里也一定会有默认浅拷贝的的问题。

#include <iostream>
using namespace std;
class stack
{
public:
	stack(int capacity = 4)
	{
		_a = (int*)malloc(capacity * sizeof(int));
		if (_a == nullptr)
		{
			perror("malloc开辟失败");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~stack()
	{
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
	void Push(int x)
	{
		_a[_top] = x;
		_top++;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	stack s1;
	s1.Push(1);
	s1.Push(2);
	stack s2;
	s2.Push(3);
	s2.Push(4);
	s1 = s2;
	return 0;
}

在这里插入图片描述
同样,这里还是深浅拷贝的问题。
在这里插入图片描述
这里析构一个位置了两次,所以我们要自己写一个。
注意:这和拷贝不一样,一个是将创建完毕的对象的值拷贝到正在创建中对象的里面,不用考虑两块空间大小是否相同,但是赋值不同,如果一个对象空间过大,另一个对象就会产生越界访问的问题。
那么这个问题应该如何解决呢?
如果去realloc,那么两块空间差距太大的话,就会产生很多的内存碎片。
在这里插入图片描述
其实核心思路是赋值,那么s1._a中的内容包括空间本身都是可以舍弃的,所以我们可以直接释放掉原来的空间,然后按照赋值空间的大小重新开辟一个在进行赋值。
在这里插入图片描述

#include <iostream>
using namespace std;
class stack
{
public:
	stack(int capacity = 4)
	{
		_a = (int*)malloc(capacity * sizeof(int));
		if (_a == nullptr)
		{
			perror("malloc开辟失败");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~stack()
	{
		cout << "析构" << endl;
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
	void Push(int x)
	{
		_a[_top] = x;
		_top++;
	}
	stack& operator=(stack& s)
	{
		free(_a);
		_a = (int*)malloc(s._capacity * sizeof(int));
		if(_a==nullptr)
		{
			perror("malloc重新开辟失败");
			exit(-1);
		}
		_top = s._top;
		_capacity = s._capacity;
		memcpy(_a, s._a, s._top * sizeof(int));
		return *this;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	stack s1;
	s1.Push(1);
	s1.Push(2);
	stack s2;
	s2.Push(3);
	s2.Push(4);
	s1 = s2;
	return 0;
}

在这里插入图片描述

在这里插入图片描述
那么,如果是自己给自己赋值是会有问题的,因为进入函数之后自己就把自己给释放掉了。
虽然不会报错,但是容易导致数据丢失,因为你释放掉这块空间,这块空间里面的数据就不一定得到保证,并且自己赋值自己没有必要进行任何操作,所以我们在进行释放等等操作之前进行一个判断是最好的。

#include <iostream>
using namespace std;
class stack
{
public:
	stack(int capacity = 4)
	{
		_a = (int*)malloc(capacity * sizeof(int));
		if (_a == nullptr)
		{
			perror("malloc开辟失败");
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	~stack()
	{
		cout << "析构" << endl;
		free(_a);
		_a = nullptr;
		_top = 0;
		_capacity = 0;
	}
	void Push(int x)
	{
		_a[_top] = x;
		_top++;
	}
	stack& operator=(stack& s)
	{
		cout << "赋值" <<endl;
		if (this != &s)//比较两个对象的地址是否相同,如果相同就是同一个对象
		{
			free(_a);
			_a = (int*)malloc(s._capacity * sizeof(int));
			if (_a == nullptr)
			{
				perror("malloc重新开辟失败");
				exit(-1);
			}
			_top = s._top;
			_capacity = s._capacity;
			memcpy(_a, s._a, s._top * sizeof(int));
		}
		return *this;
	}
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	stack s1;
	s1.Push(1);
	s1.Push(2);
	stack s2;
	s2.Push(3);
	s2.Push(4);
	s1 = s1;
	return 0;
}

那么成员里面是自定义类型的对象进行赋值也是和拷贝一样的,是调用自定义类型里面的赋值。
在这里插入图片描述

前置后置++重载

按照operator特性,要把运算符放到后面,那么怎么区分前置++和后置++呢?
因为前置后置++只是+1而已,所以没必要带参数(当然没算this指针)。

#include <iostream>
using namespace std;
class N
{
public:
	N(int x)
	{
		_a = x;
	}
	N& operator++()//前置++
	{
		++_a;
		return *this;
	}
	N operator++(int)//后置++,返回值不能用引用,因为返回的是临时数据
	{
		N s(*this);
		++_a;
		return s;//因为是后置++,所以返回的应该是++之前的数据
	}
	void print()
	{
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	N s1(1);
	++s1;
	s1.print();//打印的是前置++的值
	N x(0);//用来接收临时值
	N s2(1);
	x = s2++;
	x.print();//打印的是后置++返回的临时值
	s2.print();//打印的是后置++完毕的值
	return 0;
}

C++规定,在参数中加一个int就是后置++,不加就是前置++。
前置++和后置++的返回值不一样,这里要注意,因为前置++是先++后使用,所以返回++完毕的值就可以了,后置++是先使用后++,所以就需要先用一个同类的临时对象来储存需要后置++的对象,然后再将需要后置++的对象进行++,然后返回临时对象,返回的时候不能用引用,因为出了函数临时对象就被销毁了。
在这里插入图片描述

const成员函数

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

#include <iostream>
using namespace std;
class N
{
public:
	N(int x)
	{
		_a = x;
	}
	void print()
	{
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	const N s(1);//这里初始化的对象s有了const属性
	s.print();
	return 0;
}

在这里插入图片描述
在const属性的对象当中调用成员函数,因为成员函数有隐藏的this指针,格式是 N* const this,意思是this指针指向的位置不能改变,而加了const 的对象s是const N* this,不允许修改this指针指向的内容。
这里就属于权限放大。
想解决问题可以变成这种格式

const N* const this

权限缩小是可以的。
这时候我们只需要在你调用的函数后面加上const就可以了。

#include <iostream>
using namespace std;
class N
{
public:
	N(int x)
	{
		_a = x;
	}
	void print()const//这里就等同于在this指针前面加了一个const
	{
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	const N s(1);
	s.print();
	return 0;
}

在这里插入图片描述

取地址及const取地址操作符重载

这个几乎不用自己去写,其实就是取对象的地址,默认的也是一样的效果。

#include <iostream>
using namespace std;
class N
{
public:
	N(int x)
	{
		_a = x;
	}
	N* operator&()//普通
	{
		return this;
	}
	const N* operator&()const//如果这里的返回值不加const就是权限放大
	{
		return this;
	}
private:
	int _a;
};
int main()
{
	N s1(1);
	const N s2(1);
	cout << &s1 << endl;
	cout << &s2 << endl;
	return 0;
}

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ℳℓ白ℳℓ夜ℳℓ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值