类和对象|六个默认成员函数|const成员函数|运算符重载

news2024/11/16 17:30:27

文章目录

  • 默认成员构造函数
    • 1. 构造函数
      • 1.1 概念
      • 1.2 特性
    • 2. 析构函数
      • 2.1 概念
      • 2.2 特性
    • 3. 拷贝构造函数
      • 3.1 概念
      • 3.2 特性
    • 4. 运算符重载
      • 4.1 赋值重载
      • 4.2 自增自减重载
      • 4.3 取地址操作符重载
    • 5. const成员函数
    • 6. 取地址重载

默认成员构造函数

上一节我们说过,空类的大小是1字节用来占位,那空类是不是真的什么都没有呢?

并不是,C++中的任何一个类都具有6个默认成员函数在这里插入图片描述

即使它是空类,它也拥有这6个默认成员函数,下面我们依次介绍这些默认成员函数~

1. 构造函数

1.1 概念

构造函数是当对象定义时编译器默认调用的,用来完成对对象属性初始化的工作。

我们一开始写的class Date是这样

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

int main()
{
    Date d1,d2;
    d1.Init(2023, 7, 21);
    d2.Init(2023, 7, 22);
    d1.Print();
    d2.Print();
}

每定义一个对象都需要调用初始化函数初始化对象的属性,如果我们哪一次忘记了初始化,那么再访问该属性时便会访问随机值,因此有没有一种办法定义对象时直接初始化呢?这样就我们就不会忘记初始化了

我们可以定义构造函数帮我们完成这件事:
构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次

1.2 特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象

特征如下

  1. 函数名与类名相同
  2. 无返回值类型
  3. 对象实例化时编译器自动调用对应的构造函数
  4. 构造函数可以重载(本质就是我们可以写多个构造函数,提供多种初始化方式)

class Date的构造函数

class Date
{
   private:
   int _year;
   int _month;
   int _day;
   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 << _year << " " << _month << " " << _day << endl;
   }
};

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

运行结果:在这里插入图片描述

构造函数在对象创建的时候自动调用(对象是编译器创建的,不是构造函数创建的,构造函数只负责对创建对象的属性进行初始化操作)

带参数的构造函数

//带参数的构造函数
Date(int year, int month, int day)
{
   _year = year;
   _month = month;
   _day = day;
}
int main()
{
   Date d1;//调用无参构造函数
   Date d2(2023, 7, 21);//想要调用带参数的构造函数必须将括号写在对象名后面而不是类名后面
   d1.Print();
   d2.Print();

    // 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
     // 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
     // warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)
     Date d3();
}

**注意:**语法规定:调用带参数的构造函数必须将括号写在对象名后面

将带参构造函数和不带参构造函数合并为全缺省构造函数

	//全缺省构造函数
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

运行结果:在这里插入图片描述

若定义了全缺省构造函数,也定义了无参构造函数,调用对象时没有指定构造函数的参数编译器不知道调用哪一个构造函数(全缺省还是无参),因而会报错(报错仅仅是因为编译器不知道调用哪一个而不是语法错误)

在这里插入图片描述

class Stack

class Stack
{
private:
	int* _a;
	int _top;
	int _capacity;
public:
	//带缺省参数的构造函数
	Stack(int capacity = 4)
	{
		_capacity = capacity;
		_top = 0;
		_a = (int*)malloc(sizeof(int) * _capacity);
		assert(_a);
	}
	void Push(int val)
	{
		if (_capacity == _top)
		{
			_capacity *= 2;
			int* tmp = (int*)realloc(_a, sizeof(int) * _capacity);
			if (tmp)
				_a = tmp;
		}
		_a[_top] = val;
		_top++;
	}
	int& Top()
	{
		return _a[_top - 1];
	}
	void Pop()
	{
		assert(_top > 0);
		_top--;
	}
};

int main()
{
	Stack st;
	st.Push(1);
	st.Push(2);
	st.Push(3);
	st.Push(4);
	st.Push(5);
	cout << st.Top() << endl;
	st.Pop();
	cout << st.Top() << endl;
}

运行结果在这里插入图片描述

`通过汇编观察定义对象时调用了构造函数在这里插入图片描述

使用了构造函数可以让我们省去和C语言一样手动初始化栈,并且使用默认参数可以让我们设定初始容量,例如我们事先知道栈中要插入1000个元素,我们就可以直接这么定义栈Stack st(1000);这省去了后续扩容的消耗。

  1. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数(后面皆称为系统默认构造函数),一旦用户显式定义编译器将不再生成。

    class Date

    class Date
    {
    public:
    	/*
    	// 如果用户显式定义了构造函数,编译器将不再生成
    	Date(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类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数
    		// 将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成
    		// 无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用
    		Date d1;
    		d1.Print();
    	return 0;
    }
    

    我们没有定义构造函数,因此定义d1时编译器会调用编译器生成的默认构造函数

    打印结果在这里插入图片描述

    1. 系统默认构造函数作用

      • 系统默认构造函数对类的内置类型不做处理(语言自带的类型),有些编译器可能会进行处理,但是C++标准并没有这么规定

      • 系统默认构造函数会去调用自定义类型变量的构造函数(union,struct,class等)

        class Date

        class Time
        {
            public:
            Time()
            {
                cout << "Time()" << endl;
                _hour = 0;
                _minute = 0;
                _second = 0;
            }
            private:
            int _hour;
            int _minute;
            int _second;
        };
        class Date
        {
            private:
            // 基本类型(内置类型)
            int _year;
            int _month;
            int _day;
            // 自定义类型
            Time _t;
        };
        int main()
        {
            Date d;//调用Date类的系统默认构造函数
            return 0;
        }
        

        运行结果:<在这里插入图片描述在这里插入图片描述

        解释:
        Date类定义对象时我们会自动调用Date类的系统默认构造函数,系统默认构造函数对Date类的内置类型不做处理,调用自定义类型Time默认构造函数,因此_t的_hour,_minute,_second都会被初始化为0.

        来看一段问题代码

        class Time
        {
        public:
        	Time(int hour, int minute, int second)
        	{
        		cout << "Time()" << endl;
        		_hour = hour;
        		_minute = minute;
        		_second = second;
        	}
        private:
        	int _hour;
        	int _minute;
        	int _second;
        };
        class Date
        {
        private:
        	// 基本类型(内置类型)
        	int _year;
        	int _month;
        	int _day;
        	// 自定义类型
        	Time _t;
        };
        int main()
        {
        	Date d;//调用Date类的系统默认构造函数
        	return 0;
        }
        

        运行结果:在这里插入图片描述

        **解释:**Date只有系统默认构造函数,定义对象时调用系统默认构造函数,系统默认构造函数调用Time类的默认构造函数,但是我们已经定义了Time带参数的构造函数,因此Time类不具有默认构造函数,因而程序出错。

        注意:C++11针对系统默认构造函数不会对内置类型进行处理做了一个补丁,C++11后允许将内置成员变量在类中声明时给定默认值

        //C++11给定缺省值
        class A
        {
        public:
        	void Print()
        	{
        		cout << _a << " " << _c << endl;
        	}
        private:
        	int _a = 1;
        	char _c = 'a';
        };
        
        int main()
        {
        	A a;//调用系统默认构造函数给_a初值1,_c初值'a'
        	a.Print();
        	return 0;
        }
        
        1. 调用时可以不传参的构造函数称为默认构造函数,默认构造函数有3类:系统默认构造函数,自定义的无参构造函数,全缺省的构造函数。

          //默认构造函数
          class A
          {
          	
          public:
          	//自定义无参构造函数
          	A()
          	{
          		cout << "A()\n";
          	}
          	//全缺省构造函数
          	A(int a = 1, int b = 2)
          	{
          		_a = a;
          		_b = b;
          		cout << "A(int a, int b)\n";
          	}
          private:
          	int _a;
          	int _b;
          };
          
          int main()
          {
          	A a;//无法编译通过,因为不知道调用哪一个默认构造函数
          	return 0;
          }
          

2. 析构函数

2.1 概念

析构函数与构造函数的功能相反。**析构函数的作用是在对象销毁前后执行对象中资源清理工作(**对象销毁不是析构函数完成的就像对象的创建不是构造函数完成的一样)

2.2 特性

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

函数不能重载

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

    class Stack

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(DataType data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	// 其他方法...
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};
void TestStack()
{
	Stack s;
	s.Push(1);
	s.Push(2);
}

有了析构函数就不需要我们自己手动销毁栈了

  1. 编译器生成的默认析构函数作用

    • 对于内置类型不做处理
    • 对于自定义类型成员,调用自定义类型的析构函数
    class Time
    {
    public:
    	Time()
    	{
    		cout << "Time()" << endl;
    	}
    	~Time()
    	{
    		cout << "~Time()" << endl;
    	}
    private:
    	int _hour;
    	int _minute;
    	int _second;
    };
    class Date
    {
    public:
    	Date()
    	{
    		cout << "Date()" << endl;
    	}
    	~Date()
    	{
    		cout << "~Date()" << endl;
    	}
    private:
    	// 基本类型(内置类型)
    	int _year = 1970;
    	int _month = 1;
    	int _day = 1;
    	// 自定义类型
    	Time _t;
    };
    int main()
    {
    	Date d;
    	return 0;
    }
    

    运行结果:在这里插入图片描述

  2. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如class Date;有资源申请时,一定要写,否则会造成内存泄漏,比如class Stack

3. 拷贝构造函数

3.1 概念

创建对象时用当前已存在的对象初始化新对象称为拷贝。

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

c++规定任何自定义类型的拷贝都会调用拷贝构造函数

calss Stack 和 class Date

void fun1(Date d1)
{

}
void fun2(Stack st1)
{

}
int main()
{
	Stack st;
	Date d;
	fun1(d);//传参时调用Date类型的默认拷贝构造函数
	fun2(st);//传参时调用Stack类型的默认拷贝构造函数
	return 0;
}

运行结果:

在这里插入图片描述

调用Stack拷贝构造函数时会出现问题,原因我们后面解释。

3.2 特性

  1. 拷贝构造函数是构造函数的一个重要重载形式

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

    class Date
    {
    public:
    	Date(int year = 1900, int month = 1, int day = 1)
    	{
    		_year = year;
    		_month = month;
    		_day = day;
    	}
    	// Date(const Date& d)   // 正确写法
    	Date( Date d)//错误写法---拷贝构造函数的参数不能是Date,否则会发生无穷递归
    	{
    		_year = d._year;
    		_month = d._month;
    		_day = d._day;
    	}
    private:
    	int _year;
    	int _month;
    	int _day;
    };
    int main()
    {
    	Date d1;
    	Date d2(d1);//传参时将d1拷贝给d:Date d(d1),此时会调用拷贝构造函数,拷贝构造函数右会将d1拷贝给d:Date d(d1)引发无穷递归
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5SrIgELR-1690679807038)(images/image-20230723133609228.png)]

    传值拷贝引发的无穷递归:

在这里插入图片描述

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

    class Time
    {
    public:
    	Time()
    	{
    		_hour = 1;
    		_minute = 1;
    		_second = 1;
    	}
    	Time(const Time& t)
    	{
    		_hour = t._hour;
    		_minute = t._minute;
    		_second = t._second;
    		cout << "Time::Time(const Time&)" << endl;
    	}
    private:
    	int _hour;
    	int _minute;
    	int _second;
    };
    class Date
    {
    private:
    	// 基本类型(内置类型)
    	int _year = 1970;
    	int _month = 1;
    	int _day = 1;
    	// 自定义类型
    	Time _t;
    };
    int main()
    {
    	Date d1;
    	Date d2(d1);
    	return 0;
    }
    

    运行结果:在这里插入图片描述

    1. 默认拷贝构造函数的作用

      • 对内置类型实现字节序拷贝
        • 对自定义类型会调用自定义类型的拷贝构造函数
    2. c++拷贝自定义对象时为什么要使用拷贝构造函数,和c语言一样全部按字节序拷贝可以吗?

      不可以,例如上述class Stack,若使用默认拷贝构造函数,则对于内置类型就是字节序拷贝,这就会出现内存问题,我们来回顾一下上述代码

      class Stack
      {
      public:
      	Stack(size_t capacity = 3)
      	{
      		_array = (DataType*)malloc(sizeof(DataType) * capacity);
      		if (NULL == _array)
      		{
      			perror("malloc申请空间失败!!!");
      			return;
      		}
      		_capacity = capacity;
      		_size = 0;
      	}
      	void Push(DataType data)
      	{
      		// CheckCapacity();
      		_array[_size] = data;
      		_size++;
      	}
      	~Stack()
      	{
      		if (_array)
      		{
      			free(_array);
      			_array = NULL;
      			_capacity = 0;
      			_size = 0;
      		}
      	}
      private:
      	DataType* _array;
      	int _capacity;
      	int _size;
      };
      void fun1(Date d1)
      {
      
      }
      void fun2(Stack st1)
      {
      
      }
      int main()
      {
      	Stack st;
      	Date d;
      	fun1(d);//传参时调用Date类型的默认拷贝构造函数
      	fun2(st);//传参时调用Stack类型的默认拷贝构造函数
      	return 0;
      }
      

      如果是值拷贝。那么拷贝后st的成员_a和st1的成员_a都指向同一块空间在这里插入图片描述

      当st1对象销毁时,会调用析构函数,析构函数会将st1对象_a成员指向的空间释放,此时st对象的_a成员就是野指针了,当st对象销毁时调用析构函数释放野指针指向的空间就会引发内存问题。

      因此像Stack的类我们就需要自己重定义拷贝构造函数,实现深拷贝

      	//自定义拷贝构造函数实现深拷贝
      	Stack(const Stack& st)
      	{
      		_array = (DataType*)malloc(sizeof(DataType) * st._capacity);
      		if (nullptr == _array)
      		{
      			perror("malloc fail");
      			exit(-1);
      		}
      		memcpy(_array, st._array, sizeof(DataType) * st._size);
      		_capacity = st._capacity;
      		_size = st._size;
      	}
      int main()
      {
      	Stack st1;
      	st1.Push(1);
      	st1.Push(2);
      	st1.Push(3);
      	Stack st2(st1);//调用拷贝构造函数完成深拷贝
      	return 0;
      }
      

      运行结果:在这里插入图片描述

      1. 拷贝构造函数调用场景

        • 使用已存在的对象创建新对象
        • 函数参数为类类型对象
        • 函数返回值为类类型对象
        class Date
        {
        public:
        	Date(int year, int minute, int day)
        	{
        		cout << "Date(int,int,int):" << this << endl;
        	}
        	Date(const Date& d)
        	{
        		cout << "Date(const Date& d):" << this << endl;
        	}
        	~Date()
        	{
        		cout << "~Date():" << this << endl;
        	}
        private:
        	int _year;
        	int _month;
        	int _day;
        };
        Date Test(Date d)
        {
        	Date temp(d);
        	return temp;
        }
        int main()
        {
        	Date d1(2022, 1, 13);//拷贝构造函数
        	Test(d1);
        	return 0;
        }
        

      在这里插入图片描述


4. 运算符重载

C++为了增强代码的可读性新增了运算符重载,运算符重载是具有特殊函数名的函数,也具有返回值类型,函数名字以及参数列表,返回值的类型与普通函数相同。

函数名为:operator操作符
函数原型为:返回值类型 operator操作符(参数列表)

注意:

  1. 不能通过连接其它符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个参数为自定义类型参数
  3. 用于内置类型的运算符,其含义不能改变,例如:内置类型的+,不能改变其含义
  4. 用于类成员函数重载时,其形参看起来总是比操作数目少1,因为成员函数的第一个参数为隐藏的this指针
  5. .* :: sizeof ?: .这五个操作符不可以重载
//运算符重载
class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//运算符重载为成员函数可以访问类的私有成员
	bool operator==(const Date& d2)//实际上(Date* const this, const Date&d2)
	{
		return _year == d2._year && _month == d2._month && _day == d2._day;
	}

private:
	int _year;
	int _month;
	int _day; 
}; 
//bool operator==(const Date& d1, const Date& d2)
//{
//	return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;//由于类外部不能直接访问private成员
//	//可以通过友元函数解决,这里先将运算符重载作为成员函数.
//}

int main()
{
	Date d1(2023, 7, 23);
	Date d2(2023, 7, 21);
	cout << d1.operator==(d2);

运行结果:

在这里插入图片描述

这种调用和调用普通成员函数的方法一样看不出可读性,因此调用运算符重载时可以像内置类型一样直接使用运算符。

在这里插入图片描述

需要注意的是d1==d2会被编译器转换为d1.operator(d2) d2==d1会被编译器转换为d2.operator(d1),即操作符的左操作数是调用运算符重载的对象,也是his指针指向的对象

4.1 赋值重载

a.赋值运算符重载格式

  • 参数类型:const T&,引用传递可以提高传参效率
  • 返回值类型:T&,有返回值是为了支持连续赋值,返回引用是为了提高返回的效率。
  • 检查是否给自己赋值
  • 返回*this:要符合连续赋值的含义
class Date
{
public:
	Date(int year = 2023, int month = 7, int day = 28)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator=(const Date& date)
	{
        	if (this != &date)
            {
                _year = date._year;
		_month = date._month;
		_day = date._day;
	    }
		return *this;//出了函数后*this还存在,可以返回引用
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(2023, 7, 20);
	d2 = d1;//调用赋值重载,转化为d2.operator=(d1);
	d2.Print();
	return 0;
}

运行结果:在这里插入图片描述

b.编译器会默认生成赋值重载成员函数

如果我们没有显示定义赋值重载,则编译器会默认生成一个复制重载函数,并且该函数完成字节序拷贝,类似于默认生成的拷贝构造函数。对于class Stack这种类型,使用默认的赋值重载函数将会出错,需要使用自定义的赋值重载函数和自定义的拷贝构造函数。

c.赋值运算符只能重载为成员函数,不可以重载为全局函数

上面刚说过,每一个类都会有自己的默认赋值重载函数,如果我们将赋值重载写为全局函数,那么该类就会生成一个默认重载函数,调用时不知道调用类的赋值重载还是调用全局的赋值重载。

d.Date d; Date d2 = d属于拷贝构造函数,不会调用赋值重载

**注意:**如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

4.2 自增自减重载

自增自减分为前缀自增、后缀自增、前缀自减、后缀自减。前后缀自增都是单目运算符,那么重载时如何区分重载的对应运算符是前缀还是后缀呢?

C++规定,后缀重载时多增加一个int类型的参数,但调用该函数时不需要显示传递参数,编译器会自动传递

class A
{
public:
	A()
	{
		_a = 1;
	}
		A operator++()//重载前缀++
	{
		_a++;
		return *this;
	}
	A& operator++(int)//重载后缀++
	{
		A tmp(*this);
		_a++;
		return tmp;
	}
	void Print()
	{
		cout << _a << endl;
	}
private:
	int _a;
};
int main()
{
	A a;
	a++;//编译器转换为a.operator++()
	a.Print();
	++a;//编译器转换为a.operator++(0);
	a.Print();
}

可以看见,对于自定义类型,前置++不需要调用拷贝构造函数,后置++需要调用2次拷贝构造函数。因此对于自定义类型来说前置++的效率更高。

总结

  1. 前缀重载运算符效率高
  2. 定义后缀重载时参数列表给出一个int类型参数作为标记

4.3 取地址操作符重载

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

5. const成员函数

调用成员函数时实际上会传递this指针,this指针指向的是当前对象,我们知道,this指针在函数参数中不可以显示传递接受,那么如果我们要求当前对象的相关信息不可以被更改怎么办呢?我们需要用const修饰this指针,但是这里的const应该放在哪里呢?我们规定,const修饰成员函数时const应该放在函数参数列表最后面。

class Date
{
public:
	Date(int year = 2023, int month = 7, int day = 28)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	Date& operator=(const Date& date)//只能重载为成员函数
	{
		if (this != &date)
		{
			_year = date._year;
			_month = date._month;
			_day = date._day;
		}
		return *this;//出了函数后*this还存在,可以返回引用
	}
	void Print()const//const成员函数表明调用的对象信息不可以更改
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d;
	d.Print();//权限缩小
	const Date d1;
	d1.Print();//权限平移
	return 0;
}

运行结果:在这里插入图片描述

注意:const成员函数可以和普通成员函数同时存在,调用时优先匹配最合适的

class Date
{
public:
	Date(int year = 2023, int month = 7, int day = 28)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()const //const成员函数表明调用的对象信息不可以更改--参数为const的成员函数:既可以打印const对象,又可以打印非const对象
	{
		cout << _year << "-" << _month << "-" << _day << endl;
		cout << "void Print()const\n";
	}
	//const成员函数重载
	void Print()//参数为没有被const修饰的成员函数:只能打印非const对象
	{
		cout << _year << "-" << _month << "-" << _day << endl;
		cout << "void Print()\n";
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	const Date d2;
	d1.Print();//void Print()
	d2.Print();//void Print() const
	return 0;
}

理论上void Print() const可以打印非const对象,但是这里重载了非const的Print函数,因此d1.Print()时会优先调用最佳匹配的,也就是void Print()

关于const成员函数需要知道的几点

  1. const对象不可以调用非const成员函数
  2. 非const对象可以调用const成员函数
  3. const成员函数内部不可以调用非const成员函数
  4. 非const成员函数内部可以调用const成员函数

6. 取地址重载

//取地址重载
class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//普通取地址重载
	Date* operator&()
	{
		return this;
	}
	//const取地址重载
	const Date* operator&()const
	{
		return this;
	}
private:
	int _year;
	int _month;
	int _day;
};

取地址运算符一般不会重载,使用默认生成的即可,除非你不想让别人获取对象的地址

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

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

相关文章

行为型:发布订阅模式

定义   发布订阅模式是基于一个事件&#xff08;主题&#xff09;通道&#xff0c;希望接收通知的对象Subscriber&#xff08;订阅者&#xff09;通过自定义事件订阅主题&#xff0c;被激活事件的对象 Publisher &#xff08;发布者&#xff09;通过发布主题事件的方式通知订…

STM32F103利用CubeMX配置开启定时中断

1、外部晶振8MHz&#xff0c;下载方式SWD模式&#xff0c;需求配置定时器1&#xff0c;产生每100ms一次中断 新建工程、配置晶振、选择下载方式等略 2、查阅资料&#xff0c;STM32F103的时钟树分配 3、配置CubeMX的时钟树 4、配置定时器-开启定时中断 5、配置定时时间 &…

VoxPoser:使用大语言模型(GPT-4)来对机器人操作的可组合三维值图【论文解读】

这是最近斯坦福的李飞飞团队的一篇论文:VoxPoser: Composable 3D Value Maps for Robotic Manipulation with Language Models 主要是通过大语言模型LLM和视觉语言模型VLM结合&#xff0c;来对机器人做各种日常操作&#xff0c;我们可以先来看下实际效果&#xff1a;大语言模型…

使用LangChain构建问答聊天机器人案例实战(一)

使用LangChain构建问答聊天机器人案例实战 现场演示GPT-4代码生成 本节我们会通过一个综合案例,跟大家讲解LangChain,这个案例产生的代码会直接在浏览器中运行,并且会输出结果,如图14-1所示,用户问:“What was the highest close price of IBM?”(“IBM的最高收盘价是…

【Linux命令200例】mdel删除指定目录下的多个文件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜活的实操案例对各个命令进行深入…

基于注解手写Spring的IOC(上)

一、思路 先要从当前类出发找到对应包下的所有类文件&#xff0c;再从这些类中筛选出类上有MyComponent注解的类&#xff1b;把它们都装入Map中&#xff0c;同时类属性完成MyValue的赋值操作。 二、具体实现 测试类结构&#xff1a; 测试类&#xff1a;myse、mycontor、BigSt…

hcq1-1300-d

禾川的产品&#xff1a;版本V3.22 网口1&#xff1a; IPV4&#xff1a;192.168.188.100 子网掩码&#xff1a;255.255.255.0 网口2&#xff1a; IPV4&#xff1a;192.168.88.100 子网掩码&#xff1a;255.255.255.0 功能按键&#xff1a; 旋转拨码0 切换 SYS\IN\OUT 指示灯及…

NLP From Scratch: 基于注意力机制的 seq2seq 神经网络翻译

NLP From Scratch: 基于注意力机制的 seq2seq 神经网络翻译 这是关于“从头开始进行 NLP”的第三篇也是最后一篇教程&#xff0c;我们在其中编写自己的类和函数来预处理数据以完成 NLP 建模任务。 我们希望在完成本教程后&#xff0c;您将继续学习紧接着本教程的三本教程&…

C#,数值计算——对数正态分布(logarithmic normal distribution)的计算方法与源程序

对数正态分布&#xff08;logarithmic normal distribution&#xff09;是指一个随机变量的对数服从正态分布&#xff0c;则该随机变量服从对数正态分布。对数正态分布从短期来看&#xff0c;与正态分布非常接近。但长期来看&#xff0c;对数正态分布向上分布的数值更多一些。 …

【机器学习】Gradient Descent

Gradient Descent for Linear Regression 1、梯度下降2、梯度下降算法的实现(1) 计算梯度(2) 梯度下降(3) 梯度下降的cost与迭代次数(4) 预测 3、绘图4、学习率 首先导入所需的库&#xff1a; import math, copy import numpy as np import matplotlib.pyplot as plt plt.styl…

Pytest学习教程_装饰器(二)

前言 pytest装饰器是在使用 pytest 测试框架时用于扩展测试功能的特殊注解或修饰符。使用装饰器可以为测试函数提供额外的功能或行为。   以下是 pytest 装饰器的一些常见用法和用途&#xff1a; 装饰器作用pytest.fixture用于定义测试用例的前置条件和后置操作。可以创建可重…

读发布!设计与部署稳定的分布式系统(第2版)笔记26_安全性上

1. 安全问题 1.1. 系统违规并不总是涉及数据获取&#xff0c;有时会出现植入假数据&#xff0c;例如假身份或假运输文件 1.2. 必须在整个开发过程中持续地把安全内建到系统里&#xff0c;而不是把安全像胡椒面那样在出锅前才撒到系统上 2. OWASP 2.1. Open Web Application…

DataStructure--Basic

程序设计数据结构算法 只谈数据结构不谈算法就跟去话剧院看梁山伯与祝英台结果只有梁山伯在演&#xff0c;祝英台生病了没来一样。 本文的所有内容都出自《大话数据结构》这本书中的代码实现部分&#xff0c;建议看书&#xff0c;书中比我本文写的全。 数据结构&#xff0c;直…

论文笔记——Influence Maximization in Undirected Networks

Influence Maximization in Undirected Networks ContributionMotivationPreliminariesNotations Main resultsReduction to Balanced Optimal InstancesProving Theorem 3.1 for Balanced Optimal Instances Contribution 好久没发paper笔记了&#xff0c;这篇比较偏理论&…

【笔试强训选择题】Day32.习题(错题)解析

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01; 文章目录 前言 一、Da…

线性代数(应用篇):第五章:特征值与特征向量、第六章:二次型

文章目录 第5章 特征值与特征向量、相似矩阵(一) 特征值与特征向量1.定义2.性质3.求解(1)具体型矩阵试根法、多项式带余除法&#xff1a;三阶多项式分解因式 (2)抽象型矩阵 (二) 相似1.矩阵相似(1)定义(2)性质 2.相似对角化(1)定义(2)相似对角化的条件&#xff08;n阶矩阵A可相…

自动化运维工具——Ansible

自动化运维工具——Ansible 一、Ansible概述二、ansible 环境安装部署1.管理端安装 ansible2.ansible 目录结构3.配置主机清单4.配置密钥对验证 三、ansible 命令行模块1.command 模块2.shell 模块3.cron 模块4.user 模块5.group 模块6.copy 模块7.file 模块8.hostname 模块9&a…

LeetCode102.Binary-Tree-Level-Order-Traversal<二叉树的层序遍历>

题目&#xff1a; 思路&#xff1a; 写过N叉树的层序遍历&#xff0c;(8条消息) LeetCode429.N-Ary-Tree-Level-Order-Traversal&#xff1c;N 叉树的层序遍历&#xff1e;_Eminste的博客-CSDN博客 使用栈保存每一层的结点。然后每次当前层结束。将这一层的值添加进去res中。…

【Jetpack 之 Lifecycle】

Jetpack 之 Lifecycle 在本系列文章中&#xff0c;我们准备分析Jetpack 架构组件。首先我们从最基础的组件开始&#xff1a; Lifecycle&#xff0c; 可以说Jetpack 大部分架构组件都是基于Lifecycle 建立的&#xff0c;此也为Jetpack 架构组件的基础。 关于此此组件的介绍和简…

回答网友 修改一个exe

网友说&#xff1a;他有个很多年前的没有源码的exe&#xff0c;在win10上没法用&#xff0c;让俺看一下。 俺看了一下&#xff0c;发现是窗体设计的背景色的问题。这个程序的背景色用的是clInactiveCaptionText。clInactiveCaptionText 在win10之前的系统上是灰色&#xff0c;但…