构造函数
- 局部对象
- 堆对象
- 参数对象
- 返回对象
- 全局对象
- 静态对象
1 局部对象ps:构造函数都做了什么?(构造函数什么都没做!)
如果不自定义构造和析构函数 代码清单上是没有调用构造函数的,
class A
{
public:
/* A()
{
a=1;
b=2;
}
~A(){}
*/
int geta(int e)
{
a=1;return a;
}
char c;
int a;
int b;
};
如果自定义构造函数和析构函数 那么局部对象声明处会生成对应的汇编代码
class A
{
public:
A()
{
a=1;
b=2;
}
~A(){}
char c;
int a;
int b;
};
构造函数做了什么?什么都没做
class A
{
public:
A()
{
a=1;
b=2;
}
}
2 堆对象
class A
{
public:
~A(){}
int geta(int e)
{
a=1;return a;
}
char c;
int a;
int b;
};
int main(int argc, char* argv[])
{
A *a=new A();
printf("%d\n",a->geta(2));
system("pause");
return 0;
}
没有定义构造函数 所以new的时候也没有调用构造函数
加入delete之后
int main(int argc, char* argv[])
{
A *a=new A();
printf("%d\n",a->geta(2));
delete a;
a=NULL;
system("pause");
return 0;
}
delete关键字会间接调用析构函数
3 参数对象
上一章记录的参数对象的基本传输方式,传参其实是对象的一次拷贝
先看下浅拷贝:一旦当做参数,那么就会崩溃
class shenkaobei{
public:
char *str;
int a;
shenkaobei(){};
shenkaobei(const char * s)//初始化构造
{
str=(char*)malloc(strlen(s)+1);
strcpy(str,s);
}
~shenkaobei(){
if(str != NULL)
{
free(str);
str=NULL;
}
};
};
void qiankaobei(shenkaobei lei)
{
printf("%s\n",lei.str);
}
int main(int argc, char* argv[])
{
shenkaobei lei1("sadfsadf");
qiankaobei(lei1);//lei1 传参进去 当函数结束的时候调用lei的析构函数 把指针申请的堆清空了 那么main结束后 lei1的析构函数会报错
return 0;
}
深拷贝函数参数必须是类的引用
class shenkaobei{
public:
char *str;
int a;
shenkaobei(){};
shenkaobei(const char * s)//初始化构造
{
str=(char*)malloc(strlen(s)+1);
strcpy(str,s);
}
shenkaobei(const shenkaobei &shen)//深拷贝构造
{
this->str=(char*)malloc(strlen(shen.str)+1);
strcpy(this->str,shen.str);
}
~shenkaobei(){
if(str != NULL)
{
free(str);
str=NULL;
}
};
};
看当做参数的情况
int main(int argc, char* argv[])
{
shenkaobei lei1("sadfsadf");
qiankaobei(lei1);
qiankaobei(lei1);
qiankaobei(lei1);
return 0;
}
因为有了深拷贝函数 lei1在传参之前就会调用一次深拷贝函数 str指向的堆就又被申请出来了。PS:这就是为什么 初始化函数和深拷贝函数代码虽然一样,因为初始化函数只是在main中执行了一次。
ps:引申一个概念 这里类 等号后面直接跟了一个字符串
shenkaobei lei1="sadfsadf";
qiankaobei(lei1);
//上面=号能成立是因为
shenkaobei(const char * s)//初始化构造
{
str=(char*)malloc(strlen(s)+1);
strcpy(str,s);
}
如果删除上面的初始化构造函数的话
所以:如果想让lei1被赋值一个int类型也是可以的
shenkaobei(int i)//添加这个函数就可以用 shenkaobei lei2=3;
{
a=i;
}
但是又发现一个新问题 退出main的时候崩溃了
int main(int argc, char* argv[])
{
shenkaobei lei1="sadfsadf";
shenkaobei lei2=3;
qiankaobei(lei1);
return 0;//执行到这里程序崩溃
}
上面的问题有点搞不懂,
去掉多余代码 找问题
可能是因为初始化int数据的时候调用的是shenkaobei(int i) 并没有对str进行内存的申请,所以导致析构lei2的时候报错
解决办法:在 shenkaobei(int i)里面添加str=NULL的语句
综合上面说的,类里面有指针对象的话尽量不要再类内部对指针对象进行malloc之类的操作 既然没有申请 在析构函数中就不会free 尽量在类外部(调用处)对类的指针成员进行操作。
4 返回值对象 -和参数对象相似, 参数:进入函数前调用拷贝构造函数 返回值对象:返回的时候调用拷贝构造函数
调用方先申请一部分栈空间 用来存放返回的类对象 构造函数在被调用函数内部执行
调用方代码:
被调用函数
5 全局对象与静态对象
控制台程序真正的入口点是mainCRTStartup函数 这个函数负责初始化全局和静态对象
在_cinit函数中_initterm函数中初始化了全局对象 ps:后面的虚函数还会进行深入讲解
A c;
int main(int argc, char* argv[])
{
c.a=1;
printf("%d",c.a);
return 0;
}
call E1
call E2
atexit()会注册全局和静态对象的析构函数 在exit()中会调用这些析构函数
总结:构造函数出现的时机
很多书上都说如果自己不写构造函数那么编译器会默认提供构造函数,其实很多时候并没有。那么 在什么情况下编译器会提供默认的构造函数呢?
一:本类、本类中定义的成员对象或者父类中有虚函数
因为要初始化虚表
二:父类或本类中定义的成员对象带有构造函数
由于对象是子类,因此构造顺序是先构造父类在构造自身,而构造父类的构造函数需要在本子类的构造函数中完成,所以编译器会添加默认的构造函数。