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; // 编译失败:引用未初始化
}
};