- 构造函数(constructor):在实例对象时,系统自动调用,用来初始化对象的数据成员
构造函数声明语法:
类名(参数列表);
构造函数注意点:
- 函数名必须是类名
- 无返回值类型
- 不同参数列表
- 可以是私有
- 一个类可以存在多个不同形式的构造函数(构造函数可以重载)
#include <iostream>
#include <string.h>
using namespace std;
class Computer
{
public:
//定义默认值构造函数
//减少其他形式构造函数定义
Computer(int _hz=123,int _mem=1024)
{
cpu_hz=_hz;
memory=_mem;
}
//上面的构造函数也可以写成参数列表初始化的形式
Computer(int _hz=123,int _mem=1024):cpu_hz(_hz),memory(_mem)
{
//cpu_hz=_hz;
//memory=_mem;
}
void printcomputer()
{
cout<<computer_kind_name<<"\t"<<cpu_num<<'\t'<<cpu_hz<<"\t"<<memory<<endl;
}
private
int cpu_hz;//c11标志支持定义成员变量时赋值,但是一般不这样赋值
int memory;
};
/*类里面的函数实现可以在外面,模式如下:
//类的成员函数定义
Computer::Computer(int _hz=123,int _mem=1024):cpu_hz(_hz),memory(_mem)
{
cout<<"constructor"<<endl;
}
//类的成员函数定义
void Computer::printcomputer()
{
cout<<cpu_hz<<"\t"<<memory<<endl;
}
*/
int main(int argc, char const *argv[])
{
Computer c1;//构造一个对象,如果没有自己实现构造函数调用的是无参构造函数,这里是使用的默认构造参数
//一般类中存在一个无参构造函数,只要定义构造函数,默认构造函数不存在
Computer c2("1111");
c1.printcomputer();
c2.printcomputer();
Computer &c3=c1 ;//引用
Computer *c4=new Computer; //new Computer 动态创建匿名对象
Computer *c5=&c1;//c5是一个指针
Computer *c6=new Computer(1,2);//new Computer(1,2) 动态创建匿名对象
Computer *c7=new Computer[2];//new Computer[2] 动态创建2个匿名对象
Computer c8[2];//对象数组
Computer* c9[2];//指针数组
Computer* (*c10)[2];//数组指针
return 0;
}
2.拷贝构造函数(复制构造函数):构造新对象时,用一个对象作为引用参数.复制参数对象的成员值
类存在一个默认拷贝构造函数
调用拷贝构造函数:
- 对象初始化新对象
- 对象作为函数形参,并且是值传递
- 返回值类型是对象
类中都存在一个赋值函数
void operator =(A & obj)
#include <iostream>
#include <stdio.h>
using namespace std;
class A
{
public:
A()
{
}
A(int _a)
{
a=_a;
}
//引用类型的参数
A(const A& obj)
{
cout<<"copy constructor"<<endl;
a=obj.a;
}
//赋值函数
void operator =( A & obj)
{
cout<<"operator ="<<endl;
a=obj.a;
}
void printa()
{
cout<<a<<endl;
}
private:
int a;
};
A fun()
{
static A a(4);//静态变量没有存在栈空间
//A a(5);//这样写是存在问题的,执行return后栈空间被释放了,不能返回任何东西
return a;
}
int main(int argc, char const *argv[])
{
A a1(3);
a1.printa();
A a2(a1);//这里调用的就是拷贝构造函数,参数是另一个对象
A a3 = a1;//这里也是调用的拷贝构造函数。
A a4;
a4 = a1;//这两句话才是调用的赋值函数void operator =( A & obj)
fun();//通过程序执行结果我们可以发现:
//当函数的返回值是一个对象的时候,返回对象的时候调用了拷贝构造函数。
cout<<"----"<<endl;
A ret=fun();
const A& ret2=fun();//fun() 返回时创建一个临时对象,把函数内部对象拷贝给临时对象,并且临时对象不可以修改
return 0;
}
- 拷贝分为深拷贝和浅拷贝
浅拷贝:只单纯的拷贝对象成员值
深拷贝:拷贝对象成员值,还拷贝成员指向其它值
#include <iostream>
#include <stdio.h>
using namespace std;
class A
{
public:
A()
{
p=new int[3];
}
/*//浅拷贝
A(const A& obj)
{
p=obj.p;
cout<<"copy constructor"<<endl;
}*/
//定义深拷贝
A(const A & obj)
{
p=new int[3];//也就是在调用拷贝构造函数时,需要重新给对象分配内存,
//将被拷贝对 象内存里面的东西放在新对象里面
//不然就是浅拷贝---> 直接把被拷贝对象的地址赋给了新的对象,那么在程序结束后
//调用析构函数会多次释放同一个地址而报错。
int i;
for(i=0;i<3;i++)
{
p[i]=obj.p[i];
}
}
~A()//析构函数
{
//释放语句
if(p!=NULL)
{
delete p;
p=NULL;
}
}
private:
int *p;
};
int main(int argc, char const *argv[])
{
A a1;
A a2(a1);//浅拷贝,在对象释放时,执行析构函数,析构函数释放的是同一个堆空间,出现错误
return 0;
}
3.析构函数
定义析构函数:
~类名()
析构函数定义注意点:
- 函数名是~类名
- 无返回值类型
- 无参
- 一个类中有且只能有一个析构函数.默认存在析构函数
如果没有对象释放,系统不会调用析构函数
对象构造与析构顺序:
先构造的最后析构
#include <iostream>
#include <stdio.h>
using namespace std;
class A
{
public:
A()
{
cout<<"constructor"<<endl;
p=new int[3];
}
//定义析构函数
~A()
{
cout<<"disconstructor"<<endl;
//释放语句
delete p;
p=NULL;
}
private:
int *p;
};
void fun()
{
int i;
for(i=0;i<10;i++)
{
static A a1;//静态局部变量只初始化一次
}
}
int main(int argc, char const *argv[])
{
A a1;
A *a3=new A;
//必须手动释放对象,否则析构函数不能调用
delete a3;
a3=NULL;
fun();//只调用一次析构函数
return 0;
}