C++中构造函数与析构函数

一、简介

在C++中,构造函数是一种特殊的成员函数,用于初始化类的对象。构造函数的名称与类的名称相同,没有返回值类型,也不需要使用void关键字。构造函数可以有参数和重载,因此可以根据不同的参数列表来创建不同的对象。
构造函数在对象创建时自动调用,用于完成对象的初始化工作,例如为成员变量赋初值、分配内存等。可以在构造函数中使用成员初始化列表来为成员变量赋初值,也可以在构造函数体中进行赋值操作。

二、构造函数

C ++利用构造函数实现了对象所占内存赋值。对象初始化时强制执行构造函数,如果我们没有自己实现构造函数,编译器将会提供默认的构造函数
构造函数是一种特殊的成员函数,它主要用于为对象分配空间,进行初始化。构造函数的名字必须与类名相同,不能由用户任意命名。它可以有任意类型的参数,但不能具有返回值。它不需要用户来调用,而是在建立对象时由编译器自动执行。我们使用 new 时,会先调用 malloc 申请一块堆区空间,然后调用构造函数为申请的空间赋值。

2.1 构造函数分类

构造函数有以下几种特殊的类型:

  • 默认构造函数: 没有参数的构造函数,如果没有定义任何构造函数,则编译器会自动生成一个默认构造函数。
  • 拷贝构造函数: 用于将一个已存在的对象复制到新创建的对象中,参数为同类型的引用或常量引用。
  • 移动构造函数: 用于将一个右值引用的对象的资源转移给新创建的对象,参数为同类型的右值引用。
  • 析构函数: 用于释放对象所占用的资源,没有参数和返回值类型,名称为类名前加上“~”。

构造函数的定义和使用示例:

class MyClass{
public:
    //默认构造函数
    MyClass(){
        num = 0;
        str = "default";
    }
    //有参数的构造函数
    MyClass(int n, string s){
        num = n;
        str = s;
    }
    //拷贝构造函数
    MyClass(const MyClass& other){
        num = other.num;
        str = other.str;
    }
    //移动构造函数
    MyClass(MyClass&& other){
        num = other.num;
        str = std::move(other.str);
        //转移右值引用的资源
        other.num = 0;
        other.str.clear();
    }
    //析构函数
    ~MyClass(){
        //释放资源
    }
private:
    int num;
    string str;
};
int main() {
    MyClass obj1; // 调用默认构造函数
    MyClass obj2(10, "Hello"); // 调用有参数的构造函数
    MyClass obj3 = obj1; // 调用拷贝构造函数
    MyClass obj4 = std::move(obj2); // 调用移动构造函数
    return 0;
}

2.2 构造函数语法

  • 构造函数的名字必须与类名相同,否则编译程序将把它当做一般的成员函数来处理;
  • 构造函数没有返回值,在定义构造函数时,是不能说明它的类型的;
  • 与普通的成员函数一样,构造函数的函数体可以写在类体内,也可写在类体外;
  • 构造函数一般声明为公有成员,但它不需要也不能像其他成员函数那样被显式地调用,它是在定义对象的同时被自动调用,而且只执行一次;
  • 构造函数可以不带参数。
#include<iostream>
using namespace std;
#include<string>
#include<vector>
/*
private 私有的:只有当前类内才有权限访问这个成员(类内)
protected 受保护的:类内和子类有权限访问这个成员(类内子类)
public 公共的:所有位置都有权限访问这个成员(对象,子类,类内)
*/

class people {
private:      //私有的     不可以被对象访问
	int age;
protected:    //受保护的    内类和子类能访问
	bool Gender;
public:       //共有的类内 子类 对象都可以访问
	string name;
	//构造函数的名字和类名相同,有参数(能重载),没有返回值
	people() {
		cout << "无参构造" << endl;
	}
	people(int A) {
		cout << "调用了参数为int A的构造函数" << endl;
	}
	/*people(int A, bool G, string N) {
		age = A;
		Gender = 0;
		name = N;
		cout << "调用了参数为int A,bool G,string N的构造函数" << endl;
	}*/

	people(int age, bool Genger, string name) {
		this->age = age;//this 是个指针,指向了当前对象,this的主要作用是区分成员变量和参数
	}
};
int main() {
	people p1(12);
	people p2(12, 1, "asd");
	people p3;//调用无参构造 如果是people p() 编译器会认为是函数声明不会创建对象
	cout << -------------------------- << endl;
	//在堆区创建对象C++中使用关键字 new,释放堆区内存使用关键字delete
	people *Q = new people();//调用了无参构造函数在堆区创建了对象
	people* Q1 = new people(1);//调用了参数为int A 的构造函数创建了堆区对象
	//释放堆区内存
	delete Q;
	delete Q1;
	//在堆区创建int类型的数组
	int* int_ps = new int[3];//在堆区申请了12个字符的内存
	cout << int_ps[0] << int_ps[1] << int_ps[2] << endl;
	delete[]int_ps;//释放堆区数组要加[]
	//在堆区创建people类型的数组
	people* peo_ps = new people[3];//在堆区创建了3个people类型的对象
	delete[]peo_ps;
	return 0;
}

在建立对象的同时,采用构造函数给数据成员赋值,通常有以下量中形式:

类名 对象名[(实参表)]
Score op1(99, 100);
op1.showScore();
类名 *指针变量名 = new 类名[(实参表)]
Score *p;
p = new Score(99, 100);
p->showScore();
-----------------------
Score *p = new Score(99, 100);
p->showScore();

三、析构函数

在C++中,析构函数是类中的一种特殊成员函数,用于在对象被销毁时进行资源的释放和清理操作 (清空对象内部指针指向的堆区内存)。析构函数名称与类名相同,但在名称前边加上一个波浪号(~)。
**析构函数没有返回值,也没有参数,页不能重载。**因此
其声明形式如下:

class MyClass{
public:
    //构造函数
    MyClass();
    //析构函数
    ~MyClass();
};

当对象被销毁时,其析构函数会自动被调用。 对象的销毁可以是由程序员显式地调用 delete 操作符、delete[] 操作符或者在对象的声明周期结束时自动地调用析构函数(栈区对象自动释放,堆区对象需要手动调用 delete 或 delete[] 操作符调用)。
因为析构函数的主要作用是释放资源和清理操作,所以我们通常在析构函数中完成一些对象的清理操作,例如释放动态分配的内存、关闭文件句柄、释放网络连接等操作
下面是一个简单的示例,演示了如何在析构函数中释放动态分配的内存:

#include<iostream>
#include<string>    //C++中,string是类,里面包含了方法和属性
using namespace std;
class MyClass{
    int a;
    int b;
    int* p = NULL;
    int* p1 = NULL;
public:
    MyClass(){}
    MyClass(int a){
        p = new int(a);
        p1 = new int [a];
    }
    ~MyClass(){
        if(p) delete p;
        if(p1) delete[] p1;
        cout << "析构函数" << endl;
    }
};
int main(){
    MyClass p(5);
    MyClass* p1 = new MyClass(6);
    delete p1;
    return 0;
}

说明: 在以下情况下,当对象声明周期结束时,析构函数会被自动调用:

  • 对象在栈区创建时,当对象超出作用域范围时,它的析构函数就会自动被调用
void someFunction(){
    MyClass obj;    //创建对象
    //对象声明周期结束,离开作用域
}   //obj的析构函数自动被调用
  • 对象在堆区创建时,当使用 delete 操作符释放对象时,对象的析构函数就会被自动调用
void someFuntion(){
    MyClass* pObj = new MyClass(0;    //在堆区创建对象
    //在这里使用对象
    delete pObj;    //释放对象
    //pObj的析构函数自动被调用
}
  • 对象作为另一个对象的成员时,当包含该对象的对象被销毁时,该对象的析构函数就会被自动调用
class MyContainer{
public;
    MyContainer(){
        pObj = new MyClass();    //创建一个MyClass对象
    }
    ~Mycontainer(){
        delete pObj;    //释放MyClass对象
    }
private:
    MyClass* pObj;
};

在上面的示例中,当MyContainer对象被销毁时,他所包含的M有Class对象的析构函数也会被自动调用。
代码示例:

#include<iostream>
#include<string>//C++中 string是类,里面包含了方法 和 属性
#include<vector>
using namespace std;

class A {
public:
    //析构函数
	int* p;    //p属于对象,对象在堆区,p就在堆区
	A(int size) {
		p = new int[size];
	}//堆区没有释放,会造成内存泄漏
	//栈区对象自动释放,堆区对象手动释放,都会自动调用析构函数

    //析构函数
	~A() {
		if (p) {
			delete p;
			p = nullptr;    //将p赋值成空指针
		}
		cout << "析构函数" << endl;
	}
};

int main(){
    //A a(3);           //错误
	A* a = new A(3);    //p指针又指向一个堆区
	//不执行析构函数
	//析构函数是在释放对象时自动调用
	delete a;//释放a指向的堆区对象,从而调用析构函数
	return 0;
}//析构函数是为了释放对象里面的指针 指向堆区内存
----------------------------------------------------------------------------------
//输出“析构函数”

四、默认的构造函数和析构函数

默认构造函数和析构函数是C++中每个类都具有的饮食成员函数,如果没有显示定义,则会按照编译器提供的默认实现执行。

4.1 作用

  • 默认构造函数(没有参数的构造函数): 在创建类对象时,用于初始化对象的成员变量。如果没有显式定义构造函数,编译器会提供一个默认的构造函数,该函数不带任何参数,并将对象的成员变量初始化为默认值(比如数值类型初始化为0、指针类型初始化为nullptr、布尔类型初始化为false等)。
  • 默认析构函数(没有参数的析构函数): 在对象生命周期结束时,用于释放对象所占有的资源。如果对象有成员变量是指针类型,那么默认析构函数只会释放这些指针所指向内存,而不会释放指针本身的内存。 如果没有显式定义析构函数,编译器会提供一个默认的析构函数,该函数不带任何参数,并释放对象所占用的内存空间。

注意:
如果类中有成员变量是指针类型,并且这些指针指向的内存是通过 new 运算符分配的,那么就需要自己编写析构函数来释放这些内存。否则类中可能会出现内存泄露问题。

扫描二维码关注公众号,回复: 15951569 查看本文章

例如,下面是一个简单的类示例,该类包含了一个整型成员变量和一个指针类型的成员变量:

class MyClass{
public:
    MyClass();    //默认构造函数
    ~MyClass();   //默认析构函数
private:
    int m_num;
    int* m_ptr;
};

在上面的示例中,如果没有显式定义Myclass的构造函数和析构函数,编译器会提供默认实现。可以通过以下方式来显式定义默认构造函数和析构函数:

MyClass::MyClass(){
    //默认构造函数的实现
}
MyClass::~MyClass(){
    //默认析构函数的实现
}

说明:

  • 对没有定义构造函数的类,其公有数据成员可以用初始化参数列表进行初始化
class MyClass{
public:
    char name[10];
    int age;
};

MyClass my= {"chen", 23};
cout << my.name << my.age << endl;

构造函数可以重载,也就是在一个类中可以定义多个不同形式的构造函数,用于创建对象是提供不同的参数选择。在创建对象时,系统会根据传递的参数类型和数量来选择调用哪一个构造函数。

例如:下面是一个定义了两个构造函数的类示例:

class MyClass{
public:
    MyClass(){
        //无参构造函数
    }
    MyClass(int value){
        //带有一个int类型参数的构造函数
        m_value = value;
    }
private:
    int m_value;
};

猜你喜欢

转载自blog.csdn.net/m0_62573214/article/details/131878049
今日推荐