C++中的对象组合:
C++中的类可以使用其他类定义成员变量。
class M
{
private:
int mI;
public:
M(int i)
{
printf("M(int i), i = %d\n",i);
mI = i;
}
int getI()
{
return mI;
}
};
class Test
{
private:
const int c; //定义一个类的成员变量的时候,不可以直接初始化
M m1;
M m2;
public:
Test()
{
printf("test()\n");
}
};
上面这段代码编译的时候回报错:
因为 const int c没有初始化, m1没有初始化,m2也没用初始化。所以Test肯定也编译不通过。
C++中提供了初始化裂变对成员变量进行初始化。
语法规则:
Constructor::Constructor(): m1(v1),m2(v1,v2),m3(v3)
{
//some other things
}
注意:
成员变量的初始化顺序与生命的顺序相关,与在初始化列表中的顺序无关;
初始化列表优先于构造函数的函数体执行。
#include <iostream>
#include <stdio.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
/*
class M
{
private:
int mI;
public:
M(int i)
{
printf("M(int i), i = %d\n",i);
mI = i;
}
int getI()
{
return mI;
}
};
class Test
{
private:
const int c; //定义一个类的成员变量的时候,不可以直接初始化
M m1;
M m2;
public:
Test():c(1),m2(3),m1(2) //初始化列表
{
printf("test()\n");
}
void print()
{
printf("c = %d, m1.mI = %d, m2.mI = %d \n ",c, m1.getI(), m2.getI());
}
~Test()
{
printf("~Test()\n");
}
};
void run()
{
Test t1;
t1.print();
Test t2;
t2.print();
}
int main(int argc, char** argv) {
run();
// const int j;
// printf("j = %d\n",j);
return 0;
}
*/
上面的代码 初始化 Test():c(1),m1(2),m2(3) 与 Test(): c(1), m2(3),m1(2)这两种格式,输出结果都是先输出m1(2)的结果,因为声明的时候先声明m1.
对于上面的代码:
小插曲1:
类中的const成员是肯定会被分配空间的。
类中的const成员变量只是一个只读变量。
编译器无法直接得到const成员变量的初始值,因此无法进入符号表成为真正意义上的常量。
小插曲2:
初始化与赋值不同:
初始化是用已经存在的对象或值对正在创建的对象进行初值设置
赋值是用已经存在的对象或值对已经存在的对象进行值设置
区别: 初始化:被初始化的对象正在创建 赋值:被赋值的对象已经存在
初始化列表主要是针对 类对象的初始化,如果是 int i;完全不用初始化列表,只有一些不可以在构造函数中初始化的才需要采用初始化列表,例如const int m;m也无法
在构造函数中进行初始化赋值,因为m是一个只读变量,所以也要采用初始化列表。
C++中的析构函数:
C++中的类可以定义一个特殊的成员函数清理对象
这个特殊的成员函数叫做析构函数
定义: ~ClassName()
析构函数没有参数也没用任何返回类型的声明
析构函数在对象销毁时自动被调用
class Test
{
private:
int c;
public:
Test()
{
printf("test()\n");
}
~Test()
{
printf("~Test()\n");
}
};
void run()
{
Test t1;//自动调用构造函数
Test t2;//自动调用构造函数
函数返回前 t2自动调用析构函数
函数返回前 t1自动调用析构函数
}
int main(int argc, char** argv) {
run();
return 0;
}
构造与析构:
构造函数与析构函数的调用秩序:
当类中有成员变量时其他类的对象时:
首先调用成员变量的构造函数(调用顺序与声明顺序相同)
之后调用自身类的构造函数
析构函数的调用秩序与对应的构造函数调用秩序相反
看一道经典例题:
class Test
{
private:
int mI;
public:
Test()
{
printf("Test()\n");
mI = -1;
}
Test(int i)
{
printf("Test(int i),i = %d\n",i);
mI = i;
}
Test(const Test& obj)
{
printf("Tset(const Test& obj), i = %d\n",obj.mI);
mI = obj.mI;
}
~Test()
{
printf("~Test(),i = %d\n",mI);
}
};
void func(Test t)
{
Test r(1);
}
void run()
{
Test t(0);
func(t);
}
int main(int argc, char** argv) {
run();
getchar();
return 0;
}
输出结果为:
Test(int i), i = 0 Test(const Test& obj), i = 0 Test(int i),i = 1 ~Test(), i = 1 ~Test(), i = 0 ~Test(), i = 0
分析:run()x下: Test t(0) 调用Test(int i) ,然后fun(t),调用这个函数的时候,实参t给形参赋值,会调用Test(const Test& obj),在func函数内部,Test r(1)又会调用Test(int i)
析构函数的顺序与这个正好相反!!!
小结 析构函数是C++中对象销毁时做清理工作的特殊函数 析构函数在对象销毁时自动被调用 析构函数是对象所使用的资源及时释放的保障 析构函数的调用秩序与构造函数相反
可以直接调用构造函数吗?直接调用会发生什么?
class Test
{
private:
int mI;
int mJ;
const char * mS;
public:
Test()
{
printf("Test()\n");
mI = 0;
mJ = 0;
}
Test(const char* s)
{
printf("Test(const char* s)\n");
Test();
mS = s;
}
~Test()
{
printf("~Test(),\n",mI);
}
void print()
{
printf("mI = %d, mJ = %d, mS = %s\n",mI,mJ,mS);
}
};
void run()
{
Test t = Test("Dujianbao");
t.print();
}
int main(int argc, char** argv) {
run();
getchar();
return 0;
}
运行上面一段代码的输出结果为:

可见:mI 与 mJ 并没有按照我们想要的运行。说明
Test(const char* s)
{printf("Test(const char* s)\n");
Test();
mS = s;
}
这里面的Test()构造函数并没有起作用。
直接调用构造函数将生产一个临时对象,但是构造函数结束之后,这个临时对象马上调用析构函数进行释放。
构造函数只可以被编译器自动调用或者生成一个对象的时候手工显示的调用,这里不属于这两中情况,所以调用无参构造函数生产一个临时对象,临时对象马上析构。
所以不要在一个构造函数当中调用另一个构造函数。
|