【C++】day04 - 【const对象和const函数】【拷贝构造函数】【静态成员】【成员指针】

一、const对象和const函数

1.1概念

	const对象就是在对象类型上加了const修饰,如:
		const Date date;
	const函数是针对成员函数的,可以在成员函数上加const修饰	
	const函数样例:
		class A{
			public:
			void show()const{//const函数	
			 
			}
			void show(){//非const函数	
			 
			}
		};

1.2 const函数相关知识点一

	const函数和非const函数可以形成重载关系。
	非const对象优先调用非const函数,没有非const函数则选择
		const函数。
	const对象只能调用const函数。
	程序举例:
#include <iostream>
using namespace std;
class A{
    
    
	public:
	void show(){
    
    //函数的汇编名字_ZN1A4showEv
		cout << "show()" << endl;
	}
	void show()const{
    
    
		//const函数的汇编名字_ZNK1A4showEv(多个字母K)
		cout << "show() const" << endl;
	}
	A(){
    
     }
};
int main(){
    
    
	A a;
	a.show();//linux终端打印出"show()",说明执行的是非const函数
	const A b;/*定义const对象。前提是类中得有一个构造
	函数才可以 */
	b.show();//linux终端打印出"show()const",说明执行的是const函数
}

1.3 const函数相关知识点二

	const函数不能对成员变量修改(只能读),也不能调用
		非const函数。
	非const函数可以调用const函数。
	如果你非要在const函数中修改成员变量,可以使用mutable。
	程序举例:
#include <iostream>
using namespace std;
class A{
    
    
	int x;
	public:
	void funx(){
    
    
		cout << "x=" << x << endl;
	}
	void show()const{
    
    
	/* funx();//这是错误的。不可以调用非const函数,
		除非funx()是const*/
	/*
		x = 100;这是错误的,不可以修改x,只能读。
		如果你非得改x的值,也可以,方法为:
			在x的定义前加mutable:
				mutable int x;
	*/
		cout << "x=" << x << endl;//查看x的值
	}
	A(){
    
     x = 100; }
};
int main(){
    
    
	const A b;
	b.show();
}

二、拷贝构造函数

2.1概念

	如果我们不提供 拷贝构造函数,则系统会提供一个默认的拷贝
	构造函数。
	拷贝构造函数目的是复制对象。
	语法为:
		类型名(const 类型名& par){ 
			
		}

2.2拷贝构造函数的调用时机

时机一:使用同类型的对象创建另外一个对象时。

	如:
		A a;
		A b=a;
		如果自己提供拷贝构造函数,则需要自己完成数据的复制。
#include <iostream>
using namespace std;
struct Date{
    
    
	int year;
	int month;
	int day;
	public:
	Date(int year=2014,int month=1,int day=1){
    
    
		cout << "Date(int,int,int)" << endl;
		this->year=year;
		this->month=month;
		this->day=day;
	}
	/*拷贝构造函数*/
	Date(const Date& date){
    
    
		cout << "Date(const,Date&)" << endl;
		year = date.year;
		month = date.month;
		day = date.day;
	}
	void show(){
    
    
		cout << year << "-" << month << "-"
		<< day << endl;
	}
};
int main(){
    
    
	Date datea;
	datea.show();
	Date dateb=datea;/*把对象dataa完全复制给对象datab。此时系统会
	调用Date(const Date& date){}函数*/
	dateb.show();
}

时机二:对象作为函数参数的值传递时

		程序举例:
		(在上例的基础上增加了showDate())
#include <iostream>
using namespace std;
struct Date{
    
    
	......
	/*拷贝构造函数*/
	Date(const Date& date){
    
    
		cout << "Date(const,Date&)" << endl;
		.....
	}
	......
};
void showDate(Date date){
    
    
	
}
int main(){
    
     
	Date datea;
	Date dateb=datea;			
	showDate(dateb);/*这条语句又复制了dateb,通过传参到showDate(),
					由于复制了dateb则系统调用了拷贝构造函数,
					即多打印了一个"Date(const,Date&)"
					但是以复制的方式传参效率低,不想复制的话,
					我们可以用指针和引用*/
}

时机三:把对象作为函数的返回值时

#include <iostream>
using namespace std;
struct Date{
    
    
	......
	/*拷贝构造函数*/
	Date(const Date& date){
    
    
		cout << "Date(const,Date&)" << endl;
		......
	}
	void show()const{
    
    
		cout << year << "-" << month << "-"
		<< day << endl;
	}
};
void showDate(const Date& date){
    
    
	
}
const Date& processDate(const Date& date){
    
    
	return date;
}
int main(){
    
     
	Date datea;
	const Date dateb=datea;
	showDate(dateb);
	processDate(dateb);/*又自动调用了拷贝构造函数,这是因为返回值
						复制了date。
						不想复制,那就使用引用和指针*/
} 

2.3为什么要自定义拷贝构造函数

	答:要自己处理对象数据复制的过程;
		当我们需要对象和对象之间都有自己独立的内存(有指针或者引用
			类型成员时);

2.4一个例子(自己设计一个数组)

#include <iostream>
using namespace std;
class Array{
    
    
	/*代表有多少个元素*/
	int size;
	/*这个数组最多能存储多少个元素*/
	int len;
	/*指向堆内存的指针*/
	int *datas;
	public:
	Array(int len = 5):size(0),len(len){
    
    
		datas = new int[len];
	}
	~Array(){
    
    
		delete[] datas;
		datas = NULL;
	}
	/*拷贝构造函数*/
	Array(const Array& a){
    
    
		cout << "Array(Array&)" << endl;
		size=a.size;
		len = a.len;
		/*根据a对象的堆内存大小 重新申请空间*/
		datas = new int[len];
		/*复制a中的堆内存中的数据*/
		for(int i=0;i<size;i++){
    
    
			datas[i] = a.datas[i];
		}
	} 
	/*提供一个函数,每次向数组中加入一个元素*/
	void push_back(int da){
    
    
		if(size >= len){
    
    
			/*可以扩容数组空间(以后再讲)*/
			/*现在我们就仅仅实现就提示你,空间满了*/
			throw "out of range index";//抛出异常
		}
		datas[size++]=da;
		//size++;
	}
	/*展现数据*/
	void show(){
    
    
		if(size == 0){
    
    
			cout << "[]" << endl;
			return;
		}
		cout << "[";
		for(int i =0;i<size-1;i++){
    
    
			cout << datas[i] << ",";
		}
		cout << datas[size-1] << "]" << endl;
	}
	
};
void foo(){
    
    
	/*const*/Array arra;
	arra.push_back(9);
	arra.push_back(5);
	arra.push_back(2);
	arra.push_back(7);
	Array arrb = arra;//调拷贝构造函数
	arrb.show();
}
int main(){
    
    
	foo();
	
}	

三、静态成员

3.1静态成员变量

	如下面的name和age都属于对象级别的数据
	class Person{
		string name; 
		int age;
		static int pcount;
		
	};
	而静态数据属于整个类型级别的数据,数据只有一份,举个例子比如:
		统计上面这个Person一种有多少个人,即上面的pcount,每定义
		一个对象(即多一个人),pcount就加一。
		pcount虽然在Person中,但其不属于类,其是static,无
		论你Person有多少个对象(人),pcount只有一份,其在全局区。
		静态成员类似于全局变量,受类型作用域显限制和权限限制。
	静态成员变量必须在类外进行初始化。
	程序举例:
#include <iostream>
using namespace std;
class Person{
    
    
	private:
	string name;
	int age;
	public:
	static int pcount;
	Person(){
    
    
		pcount++;
	}
	Person(const Person& p){
    
    /*拷贝构造函数*/
		pcount++;
	}
};

/*基本类型成员默认初始化为0
类类型成员默认调用无参构造*/
int Person::pcount=0;//pcount初始化
int main(){
    
     
	/*你可以用sizeof(Person)测一下Person的大小,你会发现pcount不属于
		Person的存储空间*/
	cout << sizeof(Person) << endl;
	
	cout << Person::pcount << endl;//0人
	Person p1;
	Person p2;
	Person p3=p1;
	cout << Person::pcount << endl;//3人
	
	cout << p1.pcount << endl;
	/*虽然pcount不属于对象,但是这样写也是可以的。但是我们
	一般不这样写。*/
}

3.2静态成员函数

	属于整个类型的函数、不使用对象就可以直接调用的函数。
	静态成员函数没有this指针。
	静态成员函数不能只直接访问非静态成员,但可以间接访问。
	程序举例:
#include <iostream>
using namespace std;
class A{
    
    
	int x;
	public:
	void show(){
    
    //可以直接访问x
		cout << "show()" << x << endl;
	}
	static void showa1(){
    
    /*不能直接访问x
		cout << "showa1()" << endl;*/
	}
	static void showa(A* mythis){
    
    /*不能直接访问x,但可
						以间接访问,即使用mythis*/
		cout << "showa()" << mythis->x << endl;
	}
};
int main(){
    
     
	A a;
	A::showa1();//wrong
	A::showa(&a);
}

3.3 写一个单例模式

	单例模式即在一个进程中,这个类型对象只有一份。比如,在win10按了
		按了很多次Ctrl+Alt+Del,仅出现一个任务管理器,这就是单例模式
		而你在win10上可以打开很多个文件资源管理器窗口,这就不是单例模式
	代码如下:
#include <iostream>
using namespace std;
	/*饿汉式(提前创建对象)  vs  懒汉式(提前创建对象)
	本例为饿汉式*/
	class Singleton{
    
    
		private:
		Singleton(){
    
    }//不允许外界调用构造函数
		Singleton(const Singleton& s){
    
    }//不允许外界调用拷贝构造函数
		static Singleton sig;//定义对象sig
		public:
		static Singleton& getInstance(){
    
    //返回对象sig 
			return sig;
		}
	};
	/*对sig进行初始化*/
	Singleton Singleton::sig;
	int main(){
    
     
		/*Singleton sig=Singleton::getInstance();错误,因为调用
							了拷贝构造函数*/
		Singleton& siga=Singleton::getInstance();
		Singleton& sigb=Singleton::getInstance();
		Singleton& sigc=Singleton::getInstance();
		cout << &siga << endl;
		cout << &sigb << endl;
		cout << &sigc << endl;
		
		/* 运行结果:三个变量的地址一样,即为单例模式*/
	}

四、成员指针

4.1成员变量指针

4.1.1概念

		指向成员变量的指针

4.1.2语法

		struct Date{
			int year;
			int month;
			int day;
		};
		/*定义成员变量指针*/
		int Date::*pmer;
		/*赋值*/
		pmer=&Date::month;//要取地址
		/*调用*/
		Date date={2014,6,7};
		date.*pmer;//值为6
		
		Date *date2 = new Date();
		date2->*pmer;

4.1.3程序举例

#include <iostream>
using namespace std;
struct Date{
    
    
	int year;
	int month;
	int day;
	Date(int year=2014,int month=6,int day=7):year(year),
	month(month),day(day){
    
    
	}
};
int main(){
    
     
	/*自定义成员变量指针*/
	int Date::*pmer;
	/*成员变量指针赋值*/
	pmer=&Date::month;
	Date date;
	/*解引用*/
	cout << date.*pmer << endl;//6
	pmer=&Date::year;
	cout << date.*pmer << endl;//2014
	Date *date2 = new Date(2015,6,7);
	/*解引用*/
	cout << date2->*pmer <<endl;
}

4.1.4成员变量指针的本质

		成员变量指针就是记录在对象中的地址偏移量 
			如上例,如果指针指向month,则偏移量为4
				如果指向day,则偏移量为8
				如果指向year,则偏移量为0
			偏移量即pmer 

4.2成员函数指针

4.2.1概念

		指向成员函数的指针

4.2.2语法

		struct Date{
			int year;
			int month;
			int day;
			int getYear(){
				return year;
			}
			int getMonth(){
				return month;
			}
		};
		int (Date::*pgetm)();//声明
		pgetm = &Date::getMonth;//赋值
		/*解引用*/
		Date date;
		(date.*pgetm)();
		
		Date *date2 = new Date();
		(date->*pgetm)();

4.2.3成员函数指针本质

		成员函数指针就是函数的绝对地址。没有偏移量这一说。

4.2.4程序举例

#include <iostream>
using namespace std;

struct Date{
    
    
	int year;
	int month;
	int day;
	Date(int year=2014,int month=6,int day=7):year(year),
	month(month),day(day){
    
    
	}
	int getYear(){
    
    
		return year;
	}
	int getMonth(){
    
    
		return month;
	}	
};
int main(){
    
     
	Date date;
	int (Date::*pmfun)();
	pmfun = &Date::getMonth;
	cout << (date.*pmfun)() << endl; //6
}

猜你喜欢

转载自blog.csdn.net/weixin_45519751/article/details/108270494