【C++面向对象】2.构造函数、析构函数

【 1. 构造函数 】

  • 类的构造函数是类的一种特殊的成员函数,它会 在每次创建类的新对象时执行
  • 构造函数的名称与类的名称是完全相同的,并且 不会返回任何类型,也不会返回 void。构造函数可 用于为某些成员变量设置初始值

1.1 带参构造函数–传入数据

  • 法1
class Complex
{
	private: 
		double x;
		double y;
	public:
		Complex(); //无参构造函数
		Complex(double, double); //带参构造函数
		void Complex_Printf(void); //输出函数
};

//在类的外部定义构造函数
Complex::Complex(double a, double b) 
{
	x = a;
	y = b;
}
class Complex
{
	private: 
		double x;
		double y;
	public:
		Complex(); //无参构造函数
		Complex(double a, double b) //在类的内部定义构造函数
		{
			x = a;
			y = b;
		}
		void Complex_Printf(void); //输出函数
};
  • 法2
//带参构造函数--形式2-赋默认值
Complex::Complex(double a=3.1, double b=6.2) 
{
	x = a;
	y = b;
}
  • 法3
//带参构造函数--法3
Complex::Complex(double a,double b) :x(a), y(b) { };
  • 法4
//带参构造函数--法4-赋默认值
Complex::Complex(double a=3.1,double b=6.2) :x(a), y(b) { };

1.2 无参构造函数–不传入数据

  • 法1
class Complex
{
	private: 
		double x;
		double y;
	public:
		Complex(); //无参构造函数
		Complex(double, double); //带参构造函数
		void Complex_Printf(void); //输出函数
};
//无参构造函数:默认值x=2,y=1
Complex::Complex()
{
	x=2;
	y=1;
};
  • 法2
//无参构造函数:默认值x=2,y=1
Complex::Complex() :x(2), y(1) { };

1.3 实例

1.3.1 实例1-单个文件类的定义方法

#include <iostream>

using namespace std;

class Box
{
private:
    double length;     // Length of a box
    double breadth;    // Breadth of a box
    double height;     // Height of a box
public:

    // 带参构造函数-法1 
    Box(double l, double b, double h)
    {
        length = l;
        breadth = b;
        height = h;
    }
    
    // 带参构造函数-法2-赋默认值
    /*Box(double l=2.0, double b=3.0, double h=4.0)
    {
       length = l;
       breadth = b;
       height = h;
    }*/

    // 带参构造函数-法3
    //Box(double l, double b, double h) :length(l), breadth(b), height(h) { };
    
    //带参构造函数-法4-赋默认值
    //Box(double l=2.0, double b=3.0, double h=4.0) :length(l), breadth(b),height(h) { };

    无参构造函数-法1
    //Box::Box()
    //{
    //   x=2;
    //   y=1;
    //};

     无参构造函数-法2
    //Box() :length(10), breadth(2), height(5) { };

    double Volume()
    {
        return length * breadth * height;
    }
};

int main(void)
{

    Box Box1(1.0, 2.2, 3.0);
    /* 适用于:
    带参构造函数-法1
    带参构造函数-法2-赋给定值
    带参构造函数法3
    带参构造函数-法4-赋给定值
    */

    //   Box Box1;
       /* 适用于:
       带参构造函数-法2-赋默认值
       带参构造函数-法4-赋默认值
       无参构造函数法1
       无参构造函数法2
       */

    cout << Box1.Volume() << endl;

    return 0;
}

1.3.2 实例2-多个文件类的定义方法

// 【Complex.h】
#pragma once

class Complex
{
	private: 
		double x;
		double y;
	public:
		Complex(); //无参构造函数
		Complex(double, double); //带参构造函数
		void Complex_Printf(void); //输出函数
};
// 【 Complex.cpp 】
#include "Complex.h"
#include <iostream>
using namespace std;

//无参构造函数:默认值x=2,y=1
Complex::Complex() :x(2), y(1) {};

//带参构造函数:赋初值
Complex::Complex(double a, double b) 
{
	x = a;
	y = b;
}

//输出函数,根据实部x和虚部y,输出对应的复数
void  Complex::  Complex_Printf(void)
{
	if      (!x && !y)        cout << '0' << endl;			     //{0}  {0}   :0
	else if (x  && !y)        cout << x << endl;                 //{≠0}{0}   :x
	else if (!x && y == -1)   cout << '-i' << endl;              //{0}  {-1}  :-i
	else if (!x && y == 1)    cout << 'i' << endl;               //{0}  {1}	  :i
	else if (!x)              cout<<y<<'i'<<endl;				 //{0}  {else}:yi
	else if (x && y == 1)     cout << x << "+i" << endl;         //{≠0}{1}   :x+i
	else if (x && y == -1)    cout << x << "-i" << endl;         //{≠0}{-1}  :x-i
	else if (y > 0)           cout << x << '+' << y << 'i'<<endl;//{≠0}{>0}  :x+yi ,y>0
	else                      cout << x <<y << 'i' << endl;		 //{≠0}{<0}  :x-yi ,y<0
}
// 【 Main.cpp 】
#include <iostream>
#include "Complex.h"
using namespace std;

int main(void)
{
	Complex a;
	Complex b(0, 0);
	Complex c(0.2, 3.7);
	Complex d(0, 1);
	Complex e(2, 0);
	Complex f(3, -5);
	a.Complex_Printf();
	b.Complex_Printf();
	c.Complex_Printf();
	d.Complex_Printf();
	e.Complex_Printf();
	f.Complex_Printf();
	return 0;
}

在这里插入图片描述

1.4 数组类的构造-在构造函数内输入数组

  • 实例
    第一行输入一个整数n,表示数组的大小,第二行输入n个整数,表示数组,最后输出所有数组元素。
#include <iostream>
using namespace std;
class Array {
private:
	int n;//数组大小 
	int* a;//数组 
public:
	// 构造函数
	Array()
	{
		cin >> n;
		a = new int[n];
		for (int j = 0; j < n; ++j)
			cin >> a[j];
	}
	// 析构函数
	~Array() {
		delete[]a;
	}
	// 输出数组
	void show() {
		for (int i = 0; i < n; i++) cout << a[i] << ' ';
	}
};
int main() {
	Array a;
	a.show();
	return 0;
}

在这里插入图片描述

【 2. 拷贝构造函数 】

  • 拷贝构造函数是一种特殊的构造函数,它在创建对象时,是 使用同一类中之前创建的对象来初始化新创建的对象。
  • 拷贝构造函数的形参必须是引用,但并不限制为const,一般普遍的会加上const限制。
  • 如果在类中没有定义拷贝构造函数,编译器会自行定义一个。如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数。
  • 拷贝构造函数通常用于:
    • 通过 使用另一个同类型的对象来初始化新创建的对象
    • 复制对象把它作为参数传递给函数。
    • 复制对象,并从函数返回这个对象。
  • 拷贝构造函数的常见形式:
classname (const classname &obj) 
{
   // 构造函数的主体
}

2.1 深拷贝、浅拷贝

  • 浅拷贝
    同一类型的对象之间可以赋值,使得 两个对象的成员变量的值相同,两个对象仍然是独立的两个对象,这种情况被称为 浅拷贝。一般情况下,浅拷贝没有任何副作用,但是 当类中有指针,并且指针指向动态分配的内存空间,析构函数做了动态内存释放的处理,会导致内存问题
  • 深拷贝
    当类中有指针,并且此指针是动态分配空间,析构函数做了 释放处理,往往需要自定义拷贝构造函数, 自行给指针动态分配空间,这种情况称为 深拷贝

2.2 实例

2.2.1 实例1 - class作为形参

  • Line line(10); // 创建一个Line类的对象line,会调用构造函数,输出 “调用构造函数”
  • display(line); // 使用 line 作为参数传入display函数中时,系统首先会调用拷贝构造函数为 line 进行复制创建得到一个临时副本,输出 “调用拷贝构造函数并为指针 ptr 分配内存”
    系统使用该副本在 display 函数中 进行操作,输出 “line 大小 :10”,display 函数结束后该副本作为局部变量被回收,输出 “释放内存”
  • 对象 Line 在主程序结束前 也将被回收,输出 “释放内存”
#include <iostream>

using namespace std;

class Line
{
   public:
      int getLength( void );
      Line( int len );             // 简单的构造函数
      Line( const Line &obj);      // 拷贝构造函数
      ~Line();                     // 析构函数

   private:
      int *ptr;
};

// 构造函数
Line::Line(int len)
{
    cout << "调用构造函数" << endl;
    // 为指针分配内存
    ptr = new int;
    *ptr = len;
}

// 拷贝构造函数 
Line::Line(const Line &obj)
{
    cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷贝值
}

// 析构函数 
Line::~Line(void)
{
    cout << "释放内存" << endl;
    delete ptr;
}

// 成员函数 
int Line::getLength( void )
{
    return *ptr;
}

// 外部函数 
void display(Line obj); 

// 程序的主函数
int main( )
{
   Line line(10);
   display(line);
   return 0;
}

// 一个外部函数 
void display(Line obj)
{
   cout << "line 大小 : " << obj.getLength() <<endl;
}

在这里插入图片描述

2.2.2 实例2 - class1=class2

  • Line line1(10); // 创建一个Line类的对象line1,会调用构造函数,输出 “调用构造函数”
  • Line line2 = line1; // 创建一个Line类的对象line2 且 line2=line1,即 lin2 是由 lin1 复制得到,即调用了拷贝构造函数,输出 “调用拷贝构造函数并为指针 ptr 分配内存”
  • display(line1); // 使用 line1 作为参数传入display函数中时,系统首先会调用拷贝构造函数为 line1 进行复制创建得到一个临时副本,输出 “调用拷贝构造函数并为指针 ptr 分配内存”
    系统使用该副本在 display 函数中 进行操作,输出 “line 大小 :10”
    display 函数结束后该副本作为局部变量被回收,输出 “释放内存”
  • 同样地, display(line2); // 使用 line2 作为参数传入display函数中时,系统首先会调用拷贝构造函数为 line2 进行复制创建得到一个临时副本,输出 “调用拷贝构造函数并为指针 ptr 分配内存”
    系统使用该副本在 display 函数中 进行操作,输出 “line 大小 :10”
    display 函数结束后该副本作为局部变量被回收,输出 “释放内存”
  • 对象 Line1 和Line2 在主程序结束前 也将被回收,分别输出 “释放内存”
#include <iostream>

using namespace std;

class Line
{
   public:
      int getLength( void );
      Line( int len );             // 简单的构造函数
      Line( const Line &obj);      // 拷贝构造函数
      ~Line();                     // 析构函数

   private:
      int *ptr;
};

// 构造函数
Line::Line(int len)
{
    cout << "调用构造函数" << endl;
    // 为指针分配内存
    ptr = new int;
    *ptr = len;
}

// 拷贝构造函数 
Line::Line(const Line &obj)
{
    cout << "调用拷贝构造函数并为指针 ptr 分配内存" << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷贝值
}

// 析构函数 
Line::~Line(void)
{
    cout << "释放内存" << endl;
    delete ptr;
}

// 成员函数 
int Line::getLength( void )
{
    return *ptr;
}

void display(Line obj); 


// 程序的主函数
int main( )
{
   Line line1(10);

   Line line2 = line1; // 这里也调用了拷贝构造函数

   display(line1);
   display(line2);

   return 0;
}

// 外部函数 
void display(Line obj)
{
   cout << "line 大小 : " << obj.getLength() <<endl;
}

在这里插入图片描述

2.2.3 实例3 - 深拷贝构造函数

  • 注意这里 为 字符串动态申请内存时,需要考虑到字符串的最后一个字符 ‘\0’new char [strlen(name)+1]
#include <iostream>
#include <cstring>
#pragma warning(disable : 4996)
using namespace std;
class Person {
    public:
        char* name; // 姓名
        int age;    // 年龄
		// 构造函数
        Person(const char* name, int age) {
            this->name = new char[strlen(name) + 1];
            strcpy(this->name, name);
            this->age = age;
        }
		// 拷贝构造函数
        Person(Person &p)
        {
            this->name=new char[strlen(p.name)+1];
            strcpy(this->name,p.name);
            this->age=p.age;
        }
		// 成员函数
        void showPerson() {
            cout << name << " " << age << endl;
        }
		// 析构函数
        ~Person() {
            if (name != nullptr) {
                delete[] name;
                name = nullptr;
            }
        }
};
int main() {
    char name[100] = { 0 };
    int age;
    cin >> name;
    cin >> age;
    Person p1(name, age);
    Person p2 = p1;
    p2.showPerson();
    return 0;
}

在这里插入图片描述

【 3. 析构函数 】

  • 类的析构函数是类的一种特殊的成员函数, 类的析构函数会在每次删除所创建的对象时执行。

  • 析构函数的 名称与类的名称是完全相同的,只是在前面加了个 波浪号(~) 作为前缀,它 不会返回任何值,也 不能带有任何参数

  • 析构函数 有助于在跳出程序(比如关闭文件、释放内存等)前释放资源

  • 如果程序里没有构造函数和析构函数,编译器在编译的时候会自动生成构造函数和析构函数,只是函数内没有任何操作。

  • 实例

#include <iostream>

using namespace std;

class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line();   // 这是构造函数声明
      ~Line();  // 这是析构函数声明

   private:
      double length;
};

// 成员函数定义,包括构造函数
Line::Line(void)
{
    cout << "Object is being created" << endl;
}
Line::~Line(void)
{
    cout << "Object is being deleted" << endl;
}

void Line::setLength( double len )
{
    length = len;
}

double Line::getLength( void )
{
    return length;
}
// 程序的主函数
int main( )
{
   Line line;

   // 设置长度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;

   return 0;
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

MR_Promethus

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

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

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

打赏作者

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

抵扣说明:

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

余额充值