类和对象(中)

发布于:2025-05-07 ⋅ 阅读:(7) ⋅ 点赞:(0)

1.c++中的匿名对象

在c++中并不是所有类型的对象都必须要有名字的,比如说我们想要得到一个数字,但又不想创建一个新的变量来浪费空间,就可以创建匿名对象。

cout<<int(1)<<endl;

以下是一个最简单的匿名对象的写法,即在类型名称旁边打一个括号,然后即完成了对匿名对象的创建。括号中是想要创建的值。此匿名对象的生命周期仅仅存在于这一行当中。

对于自定义类型的匿名对象创建如下:

class s1{
	public:
	s1()
	{
		cout<<"11"<<endl;
	}
	s1(string s1)
	{
		cout<<22<<endl;
		_name=s1;
	}
	void print()
	{
		cout<<_name<<endl;		
	}
	private:
	string _name;
	
};
int main()
{
	s1 l1("lis");
	l1.print();
	s1("lss").print();
	s1().print();
	
}

第一行代码为有名构造,其他均为匿名构造,匿名构造可以在需要调用某个函数时进行,不具有常态性。

2.对于对象动态的释放(new和delete)

c语言中使用malloc/calloc/realloc/free进行动态内存分配,malloc/calloc/realloc用来在堆上分配空间,free将申请的空间释放掉。但在c++中我们使用new和delete来实现这些功能。

c++中的new大概有四种用法,此处我们用int类型来举个例子。

int *s1= new int;//这个表示将s1空间开辟

int *s1=new int(3);//这个表示就将一个int大小的空间开辟但是将其初始化为3

int *s1=new int[3];//这个表示开辟三个空间

int* pint4 = new int[5]{ 1, 2, 3, 4 };    //这表示开辟五个空间并且分别初始化。

那么这么看newdelete和mallocfree的区别到底在哪里呢?区别在开辟自定义对象中,new和delete会自动屌用析构函数和构造函数。

#include <iostream>

using namespace std;

class Demo
{
public:
	Demo(int a1 = 10, int a2 = 20)
		: _a1(a1)
		, _a2(a2)
	{
		cout << "Demo()" << endl;
	}

	void print()
	{
		cout << _a1 << " " << _a2 << endl;
	}

	~Demo()
	{
		cout << "~Demo()" << endl;
	}

private:
	int _a1;
	int _a2;
};

void printIntArr(int* arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		cout << arr[i] << " ";
	}
	cout << endl;
}

void printDemoArr(Demo* arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		arr[i].print();
	}
	cout << endl;
}

int main()
{
	//用 new 申请一个内置类型变量的空间
	int* pint1 = new int;
	cout << *pint1 << endl; //输出 -842150451

	//使用括号中的值初始化变量
	int* pint2 = new int(5);
	cout << *pint2 << endl;	//输出 5

	//用 delete 释放一个变量的空间
	delete pint1;
	delete pint2;

	//用 new 申请一个自定义类型对象的空间,申请后会自动调用构造函数
	Demo* pd1 = new Demo;	//输出 Demo()
	pd1->print();	//输出 10 20

	//自定义类型会根据括号中的参数调用对应的构造函数
	Demo* pd2 = new Demo(5, 5);	//输出 Demo()
	pd2->print();	//输出 5 5

	//用 delete 释放一个变量的空间,释放前会自动调用析构函数
	delete pd1;	//输出 ~Demo()
	delete pd2;	//输出 ~Demo()


	//对内置类型用 new[] 开辟一块连续的空间
	int* pint3 = new int[5];	//[]中表示开辟整形的个数
	printIntArr(pint3, 5);	//输出 -842150451 -842150451 -842150451 -842150451 -842150451

	//用花括号中的值初始化开辟的连续空间,未给值的为 0
	int* pint4 = new int[5]{ 1, 2, 3, 4 };	
	printIntArr(pint4, 5);	//输出 1 2 3 4 0

	//对内置类型用 delete[] 释放一块连续的空间
	delete[] pint3;
	delete[] pint4;
	
	//对自定义类型用 new[] 开辟一块连续的空间
	//申请后会对空间自动调用构造函数 5 次
	Demo* pd3 = new Demo[5];	//输出 5 行 Demo()
	printDemoArr(pd3, 5);	//输出 5 行 10 20

	//用花括号中的值初始化开辟的连续空间
	//花括号中如果用小括号会被认为是逗号表达式,会去调用单参的构造函数
	//调用多参构造函数应在花括号中使用花括号,未给的值根据构造函数决定
	Demo* pd4 = new Demo[5]{ (1, 2), {5}, {5, 10}};	//输出 5 行 Demo()
	printDemoArr(pd4, 5);	//输出 第一行 2 20,第二行 5 10 第三行 5 10,两行 10 20

	//对自定义类型用 delete[] 释放一块连续的空间
	//释放之前会对空间自动调用析构函数 5 次
	delete[] pd3;	//输出 5 行 ~Demo
	delete[] pd4;	//输出 5 行 ~Demo

	return 0;
}

可以观察到,我们创建new demo类型的时候,会自动调用析构函数进行初始化。

3.构造函数的初始化列表

初始化列表的写法是在构造函数前面加:,然后写出需要初始化的变量例如s1,在在括号中写出需要初始化的值。

s1(int x):_date(x)//初始化列表,将x的值赋值到date中,效果和int date=x一样,只不过引用或者其他情况必须使用初始化列表。
{
	cout << 1;
}

有两种情况是必须使用初始化列表的,第一种是引用的初始化 ,第二种是const类型的初始化

class Example {
private:

	const int i;
public:
    Example(int val):i(val) { //初始化列表正确
    }
Example(int val){
i=val//这个是错误的,编译器报错
}
};
class Example {
private:
    int& ref; // 引用成员

public:
    // ✅ 正确:通过初始化列表绑定
    Example(int& val) : ref(val) {}  

    // ❌ 错误:未在初始化列表初始化
    Example(int val) { 
        ref = val; // 编译失败:引用未初始化
    }
};


网站公告

今日签到

点亮在社区的每一天
去签到