专业编程基础技术教程

网站首页 > 基础教程 正文

C++构造函数和析构函数详解 c语言构造函数和析构函数

ccvgpt 2024-10-12 13:51:36 基础教程 8 ℃
  • 类的构造函数

类的构造函数是类的一种特殊的成员函数,主要用于对象分配空间,进行初始化,它会在每次创建类的新对象时执行

构造函数的特殊性质

1)构造函数的名字必须与类名相同。

C++构造函数和析构函数详解 c语言构造函数和析构函数

2)构造函数可以有任意类型的参数,并且不会返回任何类型,也不会返回void。

3)构造函数的函数体可写在类体内,也可以在类体外。

4)构造函数可以重载,即一个类中可以定义多个参数或参数类型不同的构造函数。

5)构造函数是不能继承的。

6)构造函数被声明为公有函数,但不能像其他成员函数一样被显示调用,它会在每次创建类的新对象时执行。

7) 若在类中没有定义类的构造函数,编译器会自动生成一个无参数的默认形式的构造函数。

8) 构造函数在定义类的对象时系统自动调用,用户不能私自调用。

9)构造函数的调用顺序是在对象进入其作用域时(对象使用之前),调用构造函数。先创建的对象先调用构造函数,后创建的对象后调用构造函数。

10)类的构造函数一般作为类的公有( public )成员函数,在创建对象时可成功调用构造函数,若作为私有( private )或( protected )成员函数,在类外创建对象时是无法访问的。

11)使用没有形参的构造函数时,定义对象时不需要加括号,使用有形参的构造函数,如果形参全部有默认值,也可以不传参数,也是不用加括号。

12)构造函数除了对数据成员进行赋值外,还可以利用初始化列表对数据成员进行初始化,参数列表只需要在定义的时候写上就行了,初始化和赋值的区别在于,初始化是数据成员在定义的时候完成的(像int a = 10; 这是初始化 ),赋值是数据成员定义之后进行的( 像 int b; b = 12; 这是赋值 ),在重载的情况中,执行哪个构造函数就执行哪个初始化列表。

定义构造函数的一般形式

利用构造函数直接创建对象.其一般形式为:类名 对象名;

#include <iostream>
class Solution
{
public:
	Solution() {}
	~Solution() {}
	void set_num(int n) { num = n; }
	int get_num() { return num; }
private:
	int num;
};

int main()
{
	Solution ss;
	ss.set_num(2);
	std::cout << ss.get_num() << std::endl;
	return 0;
}

定义初始化列表的构造函数

带有成员初始化表的构造函数的一般形式如下: 类名::构造函数名(参数表): (成员初始化表){ 构造函数体 }。

类成员是按照它们在类里被声明的顺序初始化的,与它们在初始化表中列出的顺序无关。

#include <iostream>
class Solution
{
public:
	Solution() {}
	Solution(int n1, int n2) :num1(n1), num2(n2) {}
	Solution(const char* str)
	{
		int len = strlen(str);
		name = (char*)malloc(len + 1);	//注意要\0结束符
		strcpy(name, str);
	}
	~Solution()
	{
		if (nullptr != name)
		{
			free(name);
			name = nullptr;	//一定要赋值nullptr
		}
	}
	int get_sum() { return num1 + num2; }
	void print_name() { std::cout << name << std::endl; }
private:
	int num1;
	int num2;
	char* name;
};

int main()
{
	Solution ss1(1, 2);
	Solution ss2("Tom");
	std::cout << ss1.get_sum() << std::endl;
	ss2.print_name();
	return 0;
}

使用指针和new来实现创建对象

其一般语法形式为: 类名 *指针变量 = new 类名 (实参表)

#include <iostream>
class Solution
{
public:
	Solution() {}
  Solution(int n):num(n) {}
	~Solution() {}
	void set_num(int n) { num = n; }
	int get_num() { return num; }
private:
	int num;
};

int main()
{
	Solution *ss1 = new Solution;
	ss1 ->set_num(2);
	std::cout << ss1 ->get_num() << std::endl;
?
	Solution *ss2 = new Solution(4);
	std::cout << ss2 ->get_num() << std::endl;
	return 0;
}

缺省参数的构造函数

初始化对象时,输入参数可以写,也可以不写;不写时则为默认值。

#include <iostream>
class Solution
{
public:
	Solution(int a = 0) { num = a; }
	~Solution() {}
	void set_num(int n) { num = n; }
	int get_num() { return num; }
private:
	int num;
};

int main()
{
	Solution ss1;		//默认a = 0;
	std::cout << ss1.get_num() << std::endl;
	return 0;
}

拷贝构造函数

拷贝构造本质上也是构造函数;参数是所在类的常引用的构造函数;类中默认的拷贝构造函数,实现的是逐个复制非静态成员(成员的复制称为浅复制)值,复制的是成员的值,这种类中默认的拷贝构造函数实现的过程被称为浅拷贝。

(1) 用一个对象去初始化另外一个对象

#include <iostream>
class Solution
{
public:
	Solution() {}
	Solution(int n): num(n) {}
	Solution(const Solution& ss)	//拷贝构造函数
	{
		num = ss.num;
	}
	~Solution() {}

	void set_num(int n) { num = n; }
	int get_num() { return num; }
private:
	int num;
};

int main()
{
	Solution ss1(1);
	Solution ss2(2);
	ss1 = ss2;			//ss2简单赋值给ss1,不调用拷贝构造函数
	std::cout << ss1.get_num() << std::endl;
	std::cout << ss2.get_num() << std::endl;
	Solution ss3 = ss2;	//ss2初始化ss3,调用拷贝构造函数
	std::cout << ss3.get_num() << std::endl;
	Solution ss4(ss2);	//ss2初始化ss4,调用拷贝构造函数
	std::cout << ss4.get_num() << std::endl;
	return 0;
}

(2) 函数中类对象作为形参

#include <iostream>
class Solution
{
public:
	Solution() {}
	Solution(int n): num(n) {}
	Solution(const Solution& ss)
	{
		num = ss.num;
	}
	~Solution() {}

	void set_num(int n) { num = n; }
	int get_num() { return num; }
private:
	int num;
};

void function(Solution ss)
{
	ss.set_num(5);
	std::cout << ss.get_num() << std::endl;
}

int main()
{
	Solution ss1(1);
	function(ss1);	// function函数初始化形参ss会调用拷贝构造函数创建一个副本。
	return 0;
}

(3) 构造函数的匿名对象

#include <iostream>
class Solution
{
public:
	Solution() {}
	Solution(int n): num(n) {}
	Solution(const Solution& ss)
	{
		num = ss.num;
	}
	~Solution() {}

	void set_num(int n) { num = n; }
	int get_num() { return num; }
private:
	int num;
};

//functionA会返回一个新对象:匿名对象
Solution functionA()
{
	Solution A(4);
	return A;
}

void functionB()
{
	Solution ss = functionA();//用匿名对象初始化ss,此时c++编译器直接把匿名对转成ss;(扶正) 从匿名转成有名字了ss
	printf("匿名对象,被扶正,不会析构掉\n");
	ss.set_num(6);
	std::cout << ss.get_num() << std::endl;
}

void functionC()
{
	Solution ss(2);	
	ss = functionA();	//用匿名对象赋值给ss后, 匿名对象被析构
	printf("因为用匿名对象赋值给ss, 匿名对象被析构\n");
	ss.set_num(6);
	std::cout << ss.get_num() << std::endl;
}

int main()
{
	functionB();
	functionC();
	return 0;
}
  • 类的析构函数

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

2)析构函数的名称与类的名称是相同的,只是在前面加了个波浪号(~)作为前缀。

3)析构函数不会返回任何值,不能带有任何参数,也不可以被重载,可以为虚函数。

4)析构函数必须为公有public。

5)没有析构函数,编译器会自动生成默认析构函数。

6)一个类中只能拥有一个析构函数。

7)new申请内存,必须提供适当析构函数。

8)析构顺序是最后创建的对象先被析构。

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

举个例子:

#include <iostream>
class Solution
{
public:
	Solution(int n):num(n) { std::cout << "Solution constructor " << num << std::endl; }
	~Solution() { std::cout << "Solution distructor" << num << std::endl; }
private:
	int num;
};


int main()
{
	Solution ss1(1);
	Solution ss2(2);
	Solution ss3(3);
	system("pause");
	return 0;
}

运行结果:

构造函数调用顺序为:ss1->ss2->ss3

析构函数调用顺序为:ss3->ss2->ss1

最近发表
标签列表