构造与析构(C++)

2.2 构造与析构

2.2.1 构造函数

构造函数(constructor)是与类同名的特殊成员函数,主要用来初始化对象的数据成员。

像上一个例子,初始化的类成员的时候就有点麻烦。

class X{
   ...
   X (...) {//构造函数
       ...
   }
}

构造函数的特点:

  • 与类同名
  • 没有返回类型
  • 可以被重载
  • 由系统自动调用,不允许在程序中显示调用
#include <iostream>
using namespace std;

class Student{
private:
	string m_name;
	int m_age;
	int m_no;
public:
    Student(const string &name, int age, int no) {
        m_name = name;
        m_age = age;
        m_no = no;
    }
    /*类内声明*/
	void set_name(const string &name);
	void set_age(int age);
	void set_no(int no);
	void eat(const string &food);
	void who();
};

/*类外定义*/
void Student::set_name(const string &name){
		m_name = name;
	}
void Student::set_age(int age) {
		m_age = age;
	}
void Student::set_no(int no){
		m_no = no;
	}
void Student::eat(const string &food){
		cout << "我今天吃了" << food << endl;
	}
void Student::who() {
		cout << "我叫: " << m_name << " 今年: " << m_age << " 学号: " << m_no << endl;
	}

int main(void) {
	Student s1("王钢蛋", 18, 2203);
    s1.who();
	s1.eat("烙饼");
	return 0;
}

2.2.2 缺省构造函数

缺省构造函数也称无参构造函数,但其未必真的没有任何参数,为一个有参构造函数的每个参数都提供一个缺省值,同样可以达到无参构造函数的效果

注意:

  • 如果一个类没有定义任何构造函数,那么编译器会为其提供一个缺省构造函数
    • 对基本类型的成员变量,不做初始化
    • 对类类型的成员变量(成员子对象),将自动调用相应类的无参构造函数来初始化
#include <iostream>
using namespace std;

class A{

public:
	int m_i;
	A(void){
		cout << "A 的无参构造" << endl;
		m_i = 123;
	}
};

class B{
public:
	int m_j;	//基本类型成员变量
	A m_a;		//类类型成员变量(成员子对象)
};
1
int main() {
	B b; //调用成员对象m_a的无参构造函数  调用B的缺省构造函数
	
	cout << b.m_j << endl; //未知
	cout << b.m_a.m_i << endl; //123
	return 0;
}
  • 如果一个类定义了构造函数,无论是否有参数,那么编译器都不会再提供缺省构造函数

2.2.3 构造函数的重载

#include <iostream>
using namespace std;

struct param{
	int l, w, h, ww;
};

class Desk{
public:
	int length, width, height, weight;

	Desk(int l, int w, int h, int ww) {
		cout << "Desk(int,int,int,int)" << endl;
	}

	Desk(void) {
		cout << "Desk(void)" << endl;
	}

	Desk(param &p){
		cout << "Desk(param &)" << endl;
	}
};

int main(void) {

	Desk d1(1,2,3,4);
	Desk d2;
	
	param pm;
	pm.l = 1;
	pm.w = 2;
	pm.h = 3;
	pm.ww = 4;
	
	Desk d3(pm);
	return 0;
}

某些重载的构造函数具有特殊的含义:

  • 缺省构造函数:按缺省方式构造
  • 类型转换构造函数:从不同类型的对象构造
  • 拷贝构造函数:从相同类型的对象构造

2.2.4 类型转换构造函数

将其他类型转换为当前类类型需要借助转换构造函数(Conversion constructor),转换构造函数只有一个参数。

#include <iostream>
#include <cstring>
using namespace std;

class Integer {

private:
	int m_i;
public:
	Integer(void){
		cout << "Integer(void)" << endl;
		m_i = 3;
	}

	explicit Integer(int n) {
		cout << "Integer(int)" << endl;
		m_i = n;
	}

	explicit Integer(const char *str){
		cout << "Integer(const string &)" << endl;
		m_i = strlen(str);
	}
	void print(){
		cout << m_i << endl;
	}
};

int main(void) {

	Integer i;
	i.print();

	//Integer j = 5; //编译器会找参数为int类型的构造函数
	Integer j = Integer(5); //编译器会找参数为int类型的构造函数
	j.print();

	//Integer k = "hello";
	Integer k = Integer("hello");// 这样会更清晰调用的是那个构造函数
	k.print();
	return 0;
}

explicit关键字,就是告诉编译器需要类型转换时,强制要求写程如下形式:

Integer j = Integer(5);
//Integer j = 5; //error

2.2.5 拷贝构造函数

  • 用一个已定义的对象构造同类型的副本对象,将调用该类的拷贝构造函数

    class A{
        A(const A& that){ //拷贝构造函数 注意参数必须是常引用
            ...
        }
    };
    
    A a;
    A b(a);//调用拷贝构造
    A c = a;//调用拷贝构造
    

    案例:

    #include <iostream>
    using namespace std;
    
    class Data{
    public:
    	int m_data;
    
    	Data(int data = 3){
    		cout << "Data(int)" << endl;
    		m_data = data;
    	}
    
    	Data(const Data& that){ //拷贝构造函数
    		cout << "Data(const A&)" << endl;
    		m_data = that.m_data;
    	}
    };
    
    int main(void){
    	
    	Data A1;
    	Data A2(A1); //编译器会调用拷贝构造函数
    	Data A3 = A1;
    
    	return 0;
    }
    
  • 如果一个类没有显示定义拷贝构造函数,那么编译器会为其提供一个缺省拷贝构造函数

    • 对基本类型成员变量,按字节复制
    • 对类类型成员变量(成员子对象),调用相应类的拷贝构造函数
    class User {
        string m_name; //调用string类的拷贝构造函数
        int m_age;		//按字节复制
    };
    

    案例:

    #include <iostream>
    using namespace std;
    
    class A{
    public:
    	int m_a;
    	A(int m_a = 0) { //缺省构造函数
    		cout << "A(int)" << endl;
    	}
    
    	A(const A& that){ //拷贝构造函数
    		cout << "A(const A&)" << endl;
    	}
    };
    
    class B{
    public:
    	A m_b;
    };
    
    int main(void) {
    
    	B b1;		//调用A类中的缺省构造函数
    	B b2 = b1; //默认调用A类中的拷贝构造函数
    
    	return 0;
    }
    
  • 注意事项

    • 拷贝函数的调用时机
      • 用已定义对象作为同类型对象的构造实参
      • 以对象的形式向函数传递参数
      • 从函数中返回对象
    • 拷贝构造过程风险高而且效率低,设计时应尽可能避免
      • 避免或减少对象的拷贝
      • 传递对象形式的参数时,使用引用型参数
      • 从函数中返回对象,使用引用函数返回值
  • 12
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值