类中特殊的成员函数

1.构造函数

1.1功能:

在实例化对象时,完成资源的申请及对成员变量的初始化。

栈区实例化对象:

类名 类对象名(构造函数的实参表)

堆区:

类名 *类指针名 = new 类名(构造函数的实参表)

1.2格式要求

1.构造函数的名字必须与类同名

2.构造函数没有返回值

3.构造函数访问控制权限一般是public;(不一定必须,看使用场景)

1.3调用时机;

实例化对象的过程中自动调用。且只能调用一次,且不可以手动调用。

例:

#include <iostream>
using namespace std;

class Student{
    	private:
    		string name;
    		int age;
    	public:
    		//构造函数
		Student(string n, int a){
			cout<<"我是构造函数"<<endl;
			name = n;
			age = a;
		}

		void print_info(){
			cout<<this->name<<"  "<<this->age<<endl;
		}
};

int main(int argc, const char *argv[])
{
	//实参表是用来给构造函数传参的
	//从编译器的角度----->
	//s1.Student("qianxy",28);
	Student s1("qianxy",28);
	s1.print_info();

	Student *s2 = new Student("qinxy",19);
	s2->print_info();

	delete s2;
	return 0;
}

1.4构造函数可以用冒号引出 初始化表

#include <iostream>
using namespace std;

class Student{
    private:
        	string name;
        	int age;
        	int *score;
    public:
    	//构造函数用冒号引出初始化表
    	//一定要注意  :成员变量名(形参名)
    	//score(new int(s)) //初始化表中完成对指针成员的初始化
    	Student(string n, int a, int s):name(n),age(a),score(new int(s)){
    		cout<<"我是构造函数"<<endl;
    		//score = new int(s);//对于指针成员  需要先new空间再赋值
    	}

    	void print_info(){
    		cout<<this->name<<"  "<<this->age<<endl;
    	}
};

int main(int argc, const char *argv[])
{
    //实参表是用来给构造函数传参的
    //从编译器的角度----->
    //s1.Student("qianxy",28);
    Student s1("qianxy",28,99);
    s1.print_info();
    
    Student *s2 = new Student("qinxy",19,98);
    s2->print_info();
    
    delete s2;
    return 0;
}

必须使用初始化表的场景:

1.成员变量名字和构造函数形参名字冲突时;---也可以用this解决

2.类中包含 const 成员变量时

3.类中包含 引用 成员变量

4.类中包含成员子对象(类中包含其他类对象时)时,

//必须使用初始化表调用子对象的构造函数 完成对成员子对象的初始化

例1:

#include <iostream>
using namespace std;

class Student{
    	private:
    		string name;
    		int age;
    		int * const score;
    		int &sex;//0 女生  1男
	public:
		Student(string n, int a, int s, int sex):name(n),age(a),score(new int(s)),sex(sex){
			cout<<"我是构造函数"<<endl;
			//score = new int(s);//score 时被const 修饰的指针
			//只能初始化  不能赋值
			
			//this->sex = sex;//引用必须初始化
		}

		void print_info(){
			cout<<this->name<<"  "<<this->age<<endl;
		}
};

int main(int argc, const char *argv[])
{
	Student s1("qianxy",28,99,1);
	s1.print_info();
	return 0;
}
              
例2:
                #include <iostream>
using namespace std;

class Teacher{
	private:
		//string name = "马云";//新版本的编译器允许 类内 直接对成员初始化
							//但是一般不使用这种方式
		string name;
	public:
		Teacher(string n);
		string get_name(){
			return name;
		}
};
//构造函数的类内声明  类外定义
Teacher::Teacher(string n):name(n){
	cout<<"Teacher 构造"<<endl;
}	


class Person{
	private:
		Teacher t1;//成员子对象
		int age;
	public:
		//必须使用初始化表,在初始化表中调用Teacher的构造函数,完成对成员t1的初始化
		//如果没有在初始化表中显性的调用 Teacher
		//的构造函数,会默认调用Teacher类的
		//无参构造函数,如果没有无参构造函数  会报错
		Person(string n,int a):t1(n),age(a){
			cout<<"Person 构造"<<endl;
			//t1.Teacher(n);//错误的  构造函数不能被手动调用
		}
		void print_info(){
			//在Person类中无法直接访问 Teacher类的私有成员name
			//需要 Teacher类 提供public权限的函数访问
			cout<<t1.get_name()<<"  "<<age<<endl;
		}
};

int main(int argc, const char *argv[])
{
	Person p1("yangfs",28);
	p1.print_info();
	return 0;
}

1.5构造函数可以重载

我们前面用的都是有参的构造函数

构造函数是可以定义无参数版本。

如果类中没有显性给定构造函数,编译器会默认提供一个无参数的,函数体为空的构造函数,用来实例化对象。

如果类中显性的给定了构造函数,编译器就不再提供无参数的版本了

此时,如果需要使用无参构造函数,需要自己在类中手动给定

例:

#include <iostream>
using namespace std;

class Student{
    	private:
    		string name;
    		int age;
    	public:
    	Student(){//手动给定无参构造函数,否则实例化对象s1时,会报错
		cout<<"无参构造"<<endl;
	}
	Student(string n,int a):name(n),age(a){
		cout<<"有参构造"<<endl;
	}
    //构造函数可以有多个版本,只要符合重载要求就ok
    Student(string n):name(n){}
};

int main(int argc, const char *argv[])
{
	Student s1;//调用无参的
	Student s2("马云",50);//调用有参的
     Student s2("马化腾");
	return 0;
}

2.析构函数

2.1功能:

释放构造函数new出来的空间以及其他善后工作。

2.2格式:

1.与类同名,需要在名字前加上 ~ 如 ~Student(){}

2.析构函数没有返回值

3.析构函数没有参数 所以不能重载

2.3调用时机:

对象消亡时,自动调用,且只调用一次。

1.栈区

生命周期结束就消亡,调用析构函数

2.堆区

调用 delete 时,对象消亡,调用析构函数(进程结束,操作系统回收时,不会调用)

例:

#include <iostream>
using namespace std;

class Student{
	private:
		string name;
		int age;
		int *score;
	public:
		Student(){
			cout<<"无参构造"<<endl;
		}
		Student(string n, int a,int s):name(n),age(a),score(new int(s)){
			cout<<"有参构造"<<endl;
		}
		~Student();//析构函数
};

Student::~Student(){
		cout<<"析构函数"<<endl;
		delete score;//析构函数应该的干的活
		score = NULL;
}

int main(int argc, const char *argv[])
{
	//Student s1;	
	Student s2("zhangsan",18,60);

	//堆区
	Student *p1 = new Student("lisi",20,70);
	delete p1;

	return 0;
}

当类中没有显性给定析构函数时,编译器会默认提供一个

函数体为空的析构函数,用来对象消亡时使用。

如果显性的给定了析构函数,编译器就不再提供默认的了。

3.构造函数和析构函数调用的顺序

//堆区的情况不考虑 因为取决于 什么时候调用 delete

//我们只考虑栈区的情况

构造函数是顺序调用,析构函数是逆序调用,

也就是说,先构造的,后析构。

例:

#include <iostream>
using namespace std;

class Student{
    	private:
    		string name;
    		int age;
    	public:
    		Student(string n,int a):name(n),age(a){
			cout<<this->name<<"  无参构造函数"<<endl;
		}
		~Student(){
			cout<<this->name<<"  析构函数"<<endl;
		}

};

int main(int argc, const char *argv[])
{
	Student s1("s1",10);
	Student s2("s2",20);
	Student s3("s3",25);

	/*
	Student *p1 = new Student("p1",10);
	Student *p2 = new Student("p2",20);
	Student *p3 = new Student("p3",30);
	delete p1;
	delete p2;
	delete p3;
	*/
	return 0;
}

4.拷贝构造函数

4.1功能:

拷贝构造函数也是构造函数,使用来完成类对象初始化的。

4.2格式:

类名(const 类名 &other):初始化表{
    //函数体
}

4.3调用时机:

用一个已经初始化的对象去初始化新对象时,对自动调用拷贝构造函数。

string s1 = "hello";
string s2(s1);
string s2 = s1;

从编译器的角度----> string s2(s1)  <===>  s2.string(s1);

例:

#include <iostream>
using namespace std;

class Person{
    	private:
    		string name;
    		int age;
    	public:
    		Person(){
			cout<<this<<"  "<<name<<"  无参构造函数"<<endl;
		}
		Person(string n, int a):name(n),age(a){
			cout<<this<<"  "<<name<<"  有参构造函数"<<endl;
		}
		Person(const Person &other):name(other.name),age(other.age){
			cout<<this<<"  "<<name<<"  拷贝构造函数"<<endl;
		}
		~Person(){
			cout<<this<<"  "<<name<<"  析构函数"<<endl;
		}
};

int main(int argc, const char *argv[])
{
	Person p1;//无参构造
	Person p2("小明",18);//有参构造
	Person p3(p2);//拷贝构造
	Person p4 = p3;//拷贝构造
	//从编译器的角度   左调右参
	//p3.Person(p2);
	cout<<"&p1="<<&p1<<"   &p2="<<&p2<<"   &p3="<<&p3<<"   &p4="<<&p4<<endl;

	return 0;
}

5.深拷贝与浅拷贝

浅拷贝:

当类中没有显性的给定拷贝构造函数时,编译器会默认提供一个拷贝构造函数

这个拷贝构造函数只完成成员之间的简单赋值,叫做浅拷贝。

如果类中没有指针成员,使用浅拷贝是没有问题的,但是如果类中有指针成员

就会出现 double free 的问题。

深拷贝:

当类中有指针成员时,在类中显性的给定拷贝构造函数,对类中的指针成员,

重新分配资源,只完成数据的复制,这种叫做深拷贝。

参考:https://blog.csdn.net/weixin_44767670/article/details/113687780

示意图:

例:

#include <iostream>
using namespace std;

class Student{
    	private:
    		string name;
    		int *age;
    	public:
    		Student(){
			cout<<"无参构造"<<endl;
		}
		Student(string n,int a):name(n),age(new int(a)){
			cout<<"有参构造"<<endl;
		}
          //自己写的拷贝构造函数,是个深拷贝
		Student(const Student &other):
          name(other.name),age(new int(*(other.age))){
			cout<<"拷贝构造"<<endl;
		}

		~Student(){
			cout<<"析构函数"<<endl;
			delete age;
		}
		void show(){
			cout<<name<<" "<<*age<<endl;
		}
};

int main(int argc, const char *argv[])
{
	Student t1("小明",18);
	t1.show();
	Student t2 = t1;
	t2.show();
	return 0;
}

6.拷贝赋值函数

6.1格式

类名& operator=(const 类名 &other){
    if(this != &other){//防止自身给自身赋值
	//成员之间相互赋值语句
    }
    return *this;//返回自身的引用
}

6.2调用时机

两个已经初始化的对象之间相互赋值时自动调用拷贝赋值函数。

当类中没有显性的给定拷贝赋值函数时,编译器会默认提供一个拷贝赋值函数

这个拷贝赋值函数只完成成员之间的简单赋值。

拷贝赋值函数也涉及 深浅拷贝的问题。

例:

#include <iostream>
using namespace std;

class Student{
	private:
		string name;
		int age;
	public:
		Student(){
			cout<<"无参构造"<<endl;
		}
		Student(string n,int a):name(n),age(a){
			cout<<"有参构造"<<endl;
		}
		
		Student(const Student &other):name(other.name),age(other.age){
			cout<<"拷贝构造"<<endl;
		}

		//返回自身的引用是为了 可以 级联调用
		Student &operator=(const Student &other){
			cout<<"拷贝赋值"<<endl;
			if(this != &other){
				this->name = other.name;
				age = other.age;
			}
			return *this;
		}

		~Student(){
			cout<<"析构函数"<<endl;
		}
		void show(){
			cout<<name<<" "<<age<<endl;
		}
};

int main(int argc, const char *argv[])
{
	Student t1("小明",18);//有参构造
	t1.show();
	Student t2;//无参构造
	Student t3;//无参构造
	t2 = t1;//拷贝赋值
	//从编译器的角度:
	//	t2.operator=(s1);

	t3 = t2 = t1;//级联使用 = 
	//从编译器的角度:
	//	t3.operator=(t2.operator=(s1));

	t2.show();
	return 0;
}

例2:拷贝赋值函数,深拷贝的例子

#include <iostream>
using namespace std;

class Student{
    	private:
    		string name;
    		int *age;
    	public:
    		Student(){
			cout<<"无参构造"<<endl;
		}
		Student(string n,int a):name(n),age(new int(a)){
			cout<<"有参构造"<<endl;
		}
		
		Student(const Student &other):name(other.name),age(new int(*(other.age))){
			cout<<"拷贝构造"<<endl;
		}

		//返回自身的引用是为了 可以 级联调用
		Student &operator=(const Student &other){
			cout<<"拷贝赋值"<<endl;
			if(this != &other){
				this->name = other.name;
                //给指针成员重新分配空间
				age = new int(*(other.age));
			}
			return *this;
		}

		~Student(){
			cout<<"析构函数"<<endl;
           delete age;
		}
		void show(){
			cout<<name<<" "<<*age<<endl;
		}
};

int main(int argc, const char *argv[])
{
	Student t1("小明",18);//有参构造
	t1.show();
	Student t2;//无参构造
	t2 = t1;
	t2.show();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小徐的记事本

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

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

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

打赏作者

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

抵扣说明:

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

余额充值