1.思考:对象中成员变量的初始值是多少?
在栈上创建对象时,成员变量初始为随机值
在堆上创建对象时,成员变量初始为随机值
在静态存储区上创建对象时,成员变量初始为0值
#include <stdio.h>
class Test
{
private:
int i;
int j;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
};
Test gt; //静态存储区
int main()
{
printf("gt.i = %d
", gt.getI());
printf("gt.j = %d
", gt.getJ());
Test t1; //栈区
printf("t1.i = %d
", t1.getI());
printf("t1.j = %d
", t1.getJ());
Test* pt = new Test; //堆区
printf("pt->i = %d
", pt->getI()); //这里恰好是0,只能说运气
printf("pt->j = %d
", pt->getJ()); //同上
delete pt;
return 0;
}
2.构造函数
c++中可以定义与类名相同的特殊成员函数。这种特殊的成员函数叫做构造函数。
a.构造函数没有任何返回类型的声明。
b.构造函数在对象定义时自动被调用。(定义会调用构造函数,赋值不会调用构造函数)
class Test
{
private:
int i;
int j;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
Test() //构造函数
{
i = 1;
j = 2;
}
};
3.带有参数的构造函数
a.一个类中可以存在多个重载的构造函数。
b.构造函数的重载遵循c++重载的规则。
class Test
{
public:
Test()
{
printf("Test()
");
}
Test(int v)
{
printf("Test(int v), v = %d
", v);
}
};
int main()
{
Test t; //调用 Test()
Test t1(1); //调用 Test(int v)
Test t2 = 2; //调用 Test(int v)
t = t2; //最后结果只显示了上面的三个。没有显示这个,是因为构造函数只有在定义的时候(初始化)才调用,其他时候不被调用。
}
注意:
对象定义和对象声明不同:
对象定义:申请对象的空间并调用构造函数(时间存在的)
对象声明:告诉编译器存在这样一个对象(虚无的)
4.手工调用构造函数
一般情况下构造函数在对象定义时被自动调用;特殊情况下,需要手工调用构造函数。
#include <stdio.h>
class Test
{
private:
int m_value;
public:
Test()
{
printf("Test()
");
m_value = 0;
}
Test(int v)
{
printf("Test(int v), v = %d
", v);
m_value = v;
}
int getValue()
{
return m_value;
}
};
int main()
{
Test ta[3] = {Test(), Test(1), Test(2)}; //手工调用构造函数
for(int i=0; i<3; i++)
{
printf("ta[%d].getValue() = %d
", i , ta[i].getValue());
}
Test t = Test(100); //手工调用构造函数
printf("t.getValue() = %d
", t.getValue());
return 0;
}
5.特殊的构造函数
一个类需要:构造函数和拷贝构造函数
a.无参构造函数
没有参数的构造函数
当类中没定义构造函数时,编译器默认提供一个无参的构造函数,并且其函数体为空
b.拷贝构造函数
参数为const class_name&的构造函数
当类中没有定义拷贝构造函数时,编译器提供一个拷贝构造函数,课进行简单的变量的值的赋值(默认即定义了拷贝构造函数后,编译器不会继续给类提供无参构造函数)
浅拷贝:拷贝后对象的物理状态相同。(单纯的,暴力的复制)(编译器默认提供的就是浅拷贝)
深拷贝:拷贝后对象的逻辑状态相同。(复制后能达到自己想要实现的逻辑,涉及到内存就需要使用深拷贝)
eg:
浅拷贝:
#include <stdio.h>
class Test
{
private:
int i;
int j;
int *p;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
int *getP()
{
return p;
}
Test(int v)
{
i = 1;
j = 2;
p = new int;
*p = v;
}
};
int main()
{
Test t1(3);
Test t2 = t1;
printf("t1.i = %d, t1.j = %d, t1.p = %d
", t1.getI(), t1.getJ(), t1.getP());
printf("t2.i = %d, t2.j = %d, t2.p = %d
", t2.getI(), t2.getJ(), t2.getP());
return 0;
}
当在类中加入内存释放时
void free()
{
delete p;
}
出现错误。我们把一个地址中的内存释放了两次。
深拷贝:
#include <stdio.h>
class Test
{
private:
int i;
int j;
int *p;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
int *getP()
{
return p;
}
Test(int v)
{
i = 1;
j = 2;
p = new int;
*p = v;
}
Test(const Test& t)
{
i = t.i;
j = t.j;
p = new int;
*p = *t.p;
}
void free()
{
delete p;
}
};
int main()
{
Test t1(3);
Test t2 = t1;
printf("t1.i = %d, t1.j = %d, *t1.p = %d
", t1.getI(), t1.getJ(), *t1.getP());
printf("t2.i = %d, t2.j = %d, *t2.p = %d
", t2.getI(), t2.getJ(), *t2.getP());
t1.free();
t2.free();
return 0;
}
6.什么时候需要进行深拷贝?
对象中有成员代指了系统中的资源时
a.成员指向了动态内存空间;
b.成员打开了外存中的文件;
c.成员使用了系统中的网络端口;
一般原则:
只要自定义拷贝构造函数,必然是深拷贝。不然就使用默认的。