我们知道,c++类的默认构造函数有六个:
1.缺省的构造函数。
2.拷贝构造函数。(这个函数还牵扯到了深浅拷贝的问题,由于解决问题很简单,这里暂不讨论)
3.赋值语句。
4.取址运算符(operator&)(一对,一个非const的,一个const的)。
5.析构函数。
而且我们也知道,每次定义完类的对象一定会调用构造函数,程序结束后一定会调用析构函数,可是问题真的是这样的么?事实上,在我们定义了一个class的时候,如果我们没有定义构造函数,那么系统在20%的情况下是不会提供构造函数的,那么,在这个时候,这个类就会被当做内置类型来看待。那么有的人就会问了,内置类型和自定义类型的区别是什么呢?
自定义类型和内置类型最主要的区别就是是否在分配内存的时候创建对象。比如:
class Test
{
public:
Base base;
int value;
int max;
public:
};
void main()
{
int x=10;
Test t1;
}
在这里,class并没有定义构造函数,只是定义了一些class的成员。所以在运行
1.在类中提供构造函数(其实在六个默认的函数中,你提供什么函数,那么系统就会调用什么,而那些没有提供的,依旧不会调用。这往往造成一种假象,就是每次我们测试构造函数的时候都是自己提供的,所以产生假象,就是每次类的对象定义一定会调用构造函数。)
2.在类中定义一个自定义类型的变量,比如:
class Base
{
public:
Base(){};
int a;
}
class test
{
public:
Base a;
}
那么在执行 class t1 的时候,系统就会调用构造函数.因为,在Base中存在了构造函数,那么系统就会把Base看成面向对象的东西,而class test中有了面向对象的东西,自然系统也会把 test看成面向对象的来看待。
3.如果类中有虚函数的话。
注意:系统在给面向过程的对象分配空间是不创建对象的,只有在给面向对象的对象分配空间时候先分配内存在创建对象,可以类比:用malloc分配的类的对象的空间时是不创建对象的,因为malloc是面向过程的东西,对面向对象是不识别的,一股脑的把所有扔给他的东西都当面向过程来对待,所以才不会创建对象,从free中可以也可以印证,在free掉一个类的对象的时候,不会调用析构函数。但是new就不同了,new是关键字,不是函数,他是面向对象的东西,对于扔给他的东西,他是把他当成面向对象来看待的。所以在给一个类开辟空间的时候,他首先会开辟空间,然后在创建一个对象来初始化这个空间。delete也是一个面向对象的东西,那么对于他理解的一切,都是以面向对象看嗲的,所以在delete掉一个对象的时候,他会调用一个类的对象的析构函数,这也是区别free的一个方面。new的对象来用free来释放,是不会报错的,但是会引起内存的泄露,谁知道这是为什么呢??那么我问大家一个问题,如果想要开辟一个空间而不用对象来初始化他,除了用malloc函数,还有什么别的方法么?
好了扯得有点远了哈,言归正传~~~
刚说到,用Base会逼test来使用构造函数,由于Base只有构造函数,所以test也只会用构造函数,不会使用析构函数,就像骡子拉磨,打一下动一动,不打不动。你要是想要test调用默认的析构函数,你只能在Base中定义析构函数,再逼test调用析构函数。,如若不然,你什么都不定义,那么运行的时候就会发现,test什么都没有调用,偷懒了,直接把自己当成内置类型。 由于截图不方便,我没有上图,大家如果觉得不理解,你可以用编译器自己去实践一下,直接进入汇编代码查看,清晰明了。
这里再提一点:刚说到,malloc函数去开辟一个类内存的时候只是去开辟空间不去创建对象,但是没有对象我们可不可以调用类的成员呢??比如以下代码:
class test
{
public:
int a;
void func(){cout<<“hello”<<endl;}
virtual void function(){<<"hello world"<<endl;}
}
test* p=(test*)malloc(sizeof(test));
p->a(); //1
p->func();//2
p->function();//3
这段代码在输出的时候,能不能运行呢,开辟的内存中这可是没有对象啊 。
其实这个是可以运行的,这就像是struct结构一样,没有对象依旧可以用,但是呢,能运行的话到底是不是会报错呢,报错话,哪个会报错呢???
如果我运行下一个程序,会不会有错呢:
test one_test;
*p=one_test; //4
大家思考一下,接下来我会一一解答。。。
其实函数1 、2和4都是可以的,而3号函数是错误的。
malloc分配完内存尽管没有对象,但是,我们可以把他理解成一个strcut,p是一个指向struct的指针,当然可以访问成员了,struct里可以定义函数,1、2正确。对于3,我们刚才已经知道了,由于malloc开辟的空间没有调用构造函数,所以,是不能创建虚函数表的,因为虚表指针是由构造函数创造的。当你调用虚函数,系统无法获得指针,所以访问非法的。再看4,struct中重载了“=”这个运算符,你可以通过“=”来用一个struct变量来赋值。这里你就可以看到,其实在c中就有了面相对象的应用,只是没有公开说明罢了。就相当于class一样,。至于为什么把class当初struct来看,其实,在c++中,class与struct是一样的,就是对于其成员的默认访问是共有还是私有的而已,很简单吧。
申明一下,两个拥有虚函数的对象,是不能通过“=”来赋值的,因为虚函数边无法进行这样的操作。
谈了那么多,下面出几个题目看看把:
1)下面哪个内存释放方法错误?哪个正确,正确的方法中又有哪个欠佳?
void main()
{
int *p=(int *)malloc(sizeof(int)*10);
delete p; //1
delete []p; //2
free(p); //3
}
2)下面哪个内存释放方法错误?哪个正确,正确的方法中又有哪个欠佳?
void main()
{
int *p=new int[10];
delete p; //1
delete []p; //2
free(p); //3
}
3)下面哪个内存释放方法错误?哪个正确,正确的方法中又有哪个欠佳?
void main()
{
int* p=new int(10);
delete p;//1
delete []p; //2
free(p); //3
p=NULL;
free(p); //4
p=NULL;
delete[]p;//5
}
4)下面哪个内存释放方法错误?哪个正确,正确的方法中又有哪个欠佳?
class test
{
public:
int a;
};
void main()
{
test* p=new test[10];//1
delete[]p;//2
delete p;//3
free(p);//4
}
5)下面哪个内存释放方法错误?哪个正确,正确的方法中又有哪个欠佳?
class test
{
public:
int a;
~test(){}
};
void main()
{
test* p=new test[10];//1
delete[]p;//2
delete p;//3
free(p);//4
}
6)下面哪个内存释放方法错误?哪个正确,正确的方法中又有哪个欠佳?
class Base
{
public:
~Base(){}
};
class test
{
public:
Base one;
int a;
~test(){}
}
void main()
{
test* p=new test[10];//1
delete[]p;//2
delete p;//3
free(p);//4
}
这些问题你可以答对对少呢?