您的位置:首页 > 教育 > 培训 > 建筑设计总结及心得体会_上海专业seo公司_竞价托管多少钱_seo网络推广技术员招聘

建筑设计总结及心得体会_上海专业seo公司_竞价托管多少钱_seo网络推广技术员招聘

2025/4/2 17:56:05 来源:https://blog.csdn.net/2301_80326627/article/details/146880246  浏览:    关键词:建筑设计总结及心得体会_上海专业seo公司_竞价托管多少钱_seo网络推广技术员招聘
建筑设计总结及心得体会_上海专业seo公司_竞价托管多少钱_seo网络推广技术员招聘
默认成员函数就是用户没有显式实现,编译器会⾃动⽣成的成员函数称为默认成员函数。⼀个类,我 们不写的情况下编译器会默认⽣成以下6个默认成员函数。

两点要求:

第⼀:我们不写时,编译器默认⽣成的函数⾏为是什么,是否满⾜我们的需求。
第⼆:编译器默认⽣成的函数不满⾜我们的需求,我们需要⾃⼰实现,那么如何⾃⼰实现?

六个默认成员函数

  • 1.构造函数

  • 2.析构函数

  • 3.拷贝构造函数

  • 4.赋值运算符重载

  • 5.取地址运算符重载

  • 6.const成员函数

构造函数

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并 不是开空间创建对象(我们常使⽤的局部对象是栈帧创建时,空间就开好了),⽽是对象实例化时初始化 对象。

 构造函数的特点

  • 函数名与类名相同。
  • 无返回值。 (返回值啥都不需要给,也不需要写void,不要纠结,C++规定如此)
  • 对象实例化时系统会⾃动调⽤对应的构造函数
  • 构造函数可以重载
  • 如果类中没有显式定义构造函数,则C++编译器会⾃动⽣成⼀个⽆参的默认构造函数,⼀旦用户显式定义编译器将不再生成。
  • 无参构造函数、全缺省构造函数、我们不写构造时编译器默认⽣成的构造函数,都叫做默认构造函 数。但是这三个函数有且只有⼀个存在,不能同时存在。⽆参构造函数和全缺省构造函数虽然构成 函数重载,但是调⽤时会存在歧义。要注意很多同学会认为默认构造函数是编译器默认⽣成那个叫 默认构造,实际上⽆参构造函数、全缺省构造函数也是默认构造,总结下就是不传实参就可以调用的构造就叫默认构造。

  •  我们不写,编译器默认⽣成的构造,对内置类型成员变量的初始化没有要求,也就是说是是否初始化是不确定的,看编译器。对于⾃定义类型成员变量,要求调⽤这个成员变量的默认构造函数初始化。如果这个成员变量,没有默认构造函数,那么就会报错,我们要初始化这个成员变量,需要⽤初始化列表才能解决,初始化列表,我们下个章节再细细讲解。

内置类型构造函数代码:

#include<iostream>
using namespace std;
class Date{
public://无参构造函数Date(){_year = 2024;_month = 1;_day = 1;}//带参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}//全缺省构造函数Date(int year = 2024, int month = 1, int day = 1){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};

对于内置类型,编译器不要求手动实现其构造函数,若无构造函数,编译器会自动生成构造函数,但不会初始化。 

 自定义类型构造函数:

#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}
private:STDataType * _a;size_t _capacity;size_t _top;
};
// 两个Stack实现队列
class MyQueue
{
public://编译器默认⽣成MyQueue的构造函数调⽤了Stack的构造,完成了两个成员的初始化
private:Stack pushst;Stack popst;
};

对于自定义类型Stack ,需要显示写出构造函数,对于用Stack实现的MyQueue,它会自动调用Stack的构造函数,不需要手动实现构造函数。


析构函数

析构函数与构造函数功能相反,析构函数不是完成对对象本⾝的销毁,比如局部对象是存在栈帧的, 函数结束栈帧销毁,他就释放了,不需要我们管,C++规定对象在销毁时会自动调用析构函数,完成对象中资源的清理释放工作。析构函数的功能类比我们之前Stack实现的Destroy功能,而像Date没有 Destroy,其实就是没有资源需要释放,所以严格说Date是不需要析构函数的。

析构函数的特点: 

  • 析构函数名是在类名前加上字符 ~。 
  • 无参数无返回值。 (这里跟构造类似,也不需要加void)
  • ⼀个类只能有⼀个析构函数。若未显式定义,系统会自动生成默认的析构函数。 
  •  对象生命周期结束时,系统会自动调用析构函数。
  • 跟构造函数类似,我们不写编译器自动生成的析构函数对内置类型成员不做处理,⾃定类型成员会调用他的析构函数。
  •  还需要注意的是我们显示写析构函数,对于自定义类型成员也会调用他的析构,也就是说⾃定义类型成员无论什么情况都会自动调用析构函数。
  •  
    如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,如Date;如 果默认生成的析构就可以用,也就不需要显示写析构,如MyQueue;但是有资源申请时,一定要自己写析构,否则会造成资源泄漏,如Stack。
  •  ⼀个局部域的多个对象,C++规定后定义的先析构。

内置类型的析构函数 

#include<iostream>
using namespace std;
class Date{
public:~Date(){_year = 0;_month = 0;_day = 0;}
private:int _year;int _month;int _day;
};

内置类型即使不写,编译器也会自动生成一个。 


  自定义类型的析构函数

#include<iostream>
using namespace std;
typedef int STDataType;
class Stack
{
public:Stack(int n = 4){_a = (STDataType*)malloc(sizeof(STDataType) * n);if (nullptr == _a){perror("malloc申请空间失败");return;}_capacity = n;_top = 0;}// 析构函数,释放资源~Stack()
{cout << "~Stack()" << endl;free(_a);_a = nullptr;_top = _capacity = 0;
}
private:STDataType * _a;size_t _capacity;size_t _top;
};
// 两个Stack实现队列
class MyQueue
{
public://编译器默认⽣成MyQueue的析构函数调⽤了Stack的析构,释放的Stack内部的资源// 显⽰写析构,也会⾃动调⽤Stack的析构/*~MyQueue(){}*///编译器默认⽣成MyQueue的构造函数调⽤了Stack的构造,完成了两个成员的初始化
private:Stack pushst;Stack popst;
};

对于自定义类型的析构函数,像Stack类的有资源进行释放,因此需要手动实现。

MyQueue类,由Stack类和内置类型组成,不需要手动实现,进行析构时会直接调用Stack类的析构函数。


 拷贝构造函数

 如果⼀个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数 也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数。

拷贝构造函数的特点 

  • 拷贝构造函数是构造函数的一个重载。
  • 拷贝构造函数的第⼀个参数必须是类类型对象的引用,使用传值方式编译器直接报错,因为语法逻 辑上会引发无穷递归调用。 拷贝构造函数也可以多个参数,但是第⼀个参数必须是类类型对象的引用,后面的参数必须有缺省值。
  • C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以这里自定义类型传值传参和传值返回都会调用拷贝构造完成。
  • 若未显式定义拷贝构造,编译器会生成自动生成拷贝构造函数。自动生成的拷贝构造对内置类型成员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷贝),对自定义类型成员变量会调用他的拷贝构造。
  •  像Date这样的类成员变量全是内置类型且没有指向什么资源,编译器自动生成的拷贝构造就可以完 成需要的拷贝,所以不需要我们显示实现拷贝构造。像Stack这样的类,虽然也都是内置类型,但 是_a指向了资源,编译器自动生成的拷贝构造完成的值拷贝/浅拷贝不符合我们的求,所以需要 我们自己实现深拷贝(对指向的资源也进行拷贝)。像MyQueue这样的类型内部主要是自定义类型 Stack成员,编译器自动生成的拷贝构造会调⽤Stack的拷贝构造,也不需要我们显示实现 MyQueue的拷贝构造。这⾥还有⼀个小技巧,如果⼀个类显示实现了析构并释放资源,那么他就 需要显示写拷贝构造,否则就不需要。
  • 传值返回会产生一个临时对象调用拷贝构造,传值引用返回,返回的是返回对象的别名(引用),没 有产生拷贝。 但是如果返回对象是⼀个当前函数局部域的局部对象,函数结束就销毁了,那么使用引用返回是有问题的,这时的引用相当于⼀个野引用,类似一个野指针⼀样。 传引用返回可以减少拷贝,但是一定要确保返回对象,在当前函数结束后还在,才能用引用返回。

 内置类型的拷贝构造:

#include<iostream>
using namespace std;
class Date{
public:// 拷贝构造函数Date(const Date& d){_year = d._year;_month = d._month;_day = d._day;}private:int _year;int _month;int _day;
};void Func1(Date d)
{
cout << &d << endl;
d.Print();
}Date& Func2()
{
Date tmp(2024, 7, 5);
tmp.Print();
return tmp;
}int main()
{// C++规定⾃定义类型对象进⾏拷⻉⾏为必须调⽤拷⻉构造,所以这⾥传值传参要调⽤拷⻉构造Date d1(2024, 7, 5);// 所以这⾥的d1传值传参给d要调⽤拷⻉构造完成拷⻉,传引⽤传参可以较少这⾥的拷⻉Func1(d1);cout << &d1 << endl;// 这⾥可以完成拷⻉,但是不是拷⻉构造,只是⼀个普通的构造Date d2(&d1);d1.Print();d2.Print();//这样写才是拷⻉构造,通过同类型的对象初始化构造,⽽不是指针Date d3(d1);d2.Print();// 也可以这样写,这⾥也是拷⻉构造Date d4 = d1;d2.Print();// Func2返回了⼀个局部对象tmp的引⽤作为返回值// Func2函数结束,tmp对象就销毁了,相当于了⼀个野引⽤Date ret = Func2();ret.Print();return 0;    
}

自定义类型的拷贝构造函数

Stack(const Stack& st)
{// 需要对_a指向资源创建同样⼤的资源再拷⻉值_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);if (nullptr == _a){perror("malloc申请空间失败!!!");return;}memcpy(_a, st._a, sizeof(STDataType) * st._top);_top = st._top;_capacity = st._capacity;
}

 为什么进行传值传参时会无限递归

C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以这里自定义类型传值传参和传值返回都会调用拷贝构造完成。

 深\浅拷贝

 

 浅拷贝时改变一个内容的值时会影响另一个对象,因此自定义类型应采用深拷贝,不能靠编译器自己生成。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com