C++中的对象组合
C++中的类可以使用其它类定义成员变量
对象组合示例
(一个类的对象定义成另一个类的成员变量????)
const int c;//定义const成员变量,不赋初值(静态成员变量没有被初始化),后面没法初始化ops! M m1;//M类的对象作为Test的成员变量。ops! M m2;//ops!后面要初始化,调用M构造函数,没有无参构造函数,无法初始化
C++中提供了初始化列表对成员变量进行初始化
注意:
-
成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关
-
初始化列表先于构造函数的函数体执行
修改后的Test类:
class Test
{
private:
const int c;
M m1;
M m2;
public:
Test() : c(1), m2(3), m1(2)//这里顺序没作用
{
printf("构造函数在初始化列表后执行");
}
void print()
{
printf("call print");
}
};
初始化列表的使用(上一节完整程序)
#include <stdio.h>
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());
}
};
void run()
{
Test t1;
t1.print();
}
int main()
{
run();
printf("Press any key to continue...");
getchar();
return 0;
}
result:
M(int i), i = 2
M(int i), i = 3
Test()
c = 1, m1.mI = 2, m2.mI = 3
Press any key to continue...
小插曲
-
类中的const成员是肯定会被分配空间的
-
类中的const成员变量只是一个只读变量
-
编译器无法直接得到const成员变量的初始值,因此无法进入符号表成为真正意义上的常量。
初始化与赋值不同
-
初始化是用已存在的对象或值对正在创建的对象进行初值设置
-
赋值是用已存在的对象或值对已经存在的对象进行值设置
区别:
初始化:被初始化的对象正在创建
赋值:被赋值的对象已经存在
注意:
对象的销毁
生活中存在的对象都是被初始化后才上市的
生活中的对象被销毁前会做一些清理工作
如何清理被销毁的对象?
一般而言所有被销毁的对象都需要做清理
解决方案:
-
为每个类都提供一个public的destroy函数
-
对象不再被需要时立即调用destroy函数进行清理
destroy只是一个普通的函数,必须显示的调用
如果对象销毁前没有做清理,那么很可能造成资源泄漏
在构造函数中申请的资源,需要在对象销毁前释放
C++编译器是否能够自动调用某个特殊的函数进行对象的清理?
C++中的析构函数
C++中的类可以定义一个特殊的成员函数清理对象。这个特殊的成员函数叫做析构函数
-
析构函数没有参数也没有任何返回类型的声明
-
析构函数在对象销毁时自动被调用
#include <stdio.h>
class Test
{
private:
int mI;
public:
Test(int i) : mI(i)//初始化
{
printf("Test(), mI = %d\n", mI);
}
~Test()
{
printf("~Test(), mI = %d\n", mI);
}
};
void run()
{
Test t1(1);
Test t2(2);
}
int main()
{
run();
printf("Press any key to continue...");
getchar();
return 0;
}
result:
Test(), mI = 1
Test(), mI = 2
~Test(), mI = 2
~Test(), mI = 1//先入后出
Press any key to continue...
Array类的进化
Array.h
#ifndef _ARRAY_H_
#define _ARRAY_H_
class Array
{
private:
int mLength;
int* mSpace;
public:
Array(int length);//构造函数
Array(const Array& obj);//拷贝构造函数
int length();//返回 数组长度
void setData(int index, int value);//设置数组的值
int getData(int index);//获得数组的值
~Array();//析构函数
};
#endif
Array.cpp
#include "Array.h"
Array::Array(int length)
{
if( length < 0 )
{
length = 0;
}
mLength = length;
mSpace = new int[mLength];
}
Array::Array(const Array& obj)
{
mLength = obj.mLength;
mSpace = new int[mLength];
for(int i=0; i<mLength; i++)
{
mSpace[i] = obj.mSpace[i];
}
}
int Array::length()
{
return mLength;
}
void Array::setData(int index, int value)
{
mSpace[index] = value;
}
int Array::getData(int index)
{
return mSpace[index];
}
Array::~Array()//析构函数
{
mLength = -1;
delete[] mSpace;
}
main.cpp
#include <stdio.h>
#include "Array.h"
int main()
{
Array a1(10);
for(int i=0; i<a1.length(); i++)
{
a1.setData(i, i);
}
for(int i=0; i<a1.length(); i++)
{
printf("Element %d: %d\n", i, a1.getData(i));
}
Array a2 = a1;
for(int i=0; i<a2.length(); i++)
{
printf("Element %d: %d\n", i, a2.getData(i));
}
printf("Press any key to continue...");
getchar();
return 0;
}
构造与析构
构造函数与析构函数的调用秩序
构造与析构的调用秩序
#include <stdio.h>
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("Test(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()
{
run();
printf("Press any key to continue...");
getchar();
return 0;
}
result:
Test(int i), i = 0
Test(const Test& obj), i = 0 //important
Test(int i), i = 1
~Test(), i = 1
~Test(), i = 0
~Test(), i = 0
Press any key to continue...
解析:
Test(int i), i = 0 //Test t(0);调用构造函数Test(int i);
Test(const Test& obj), i = 0 //以t为实参调用func(t);发生了拷贝构造(实参去初始化形参导致的)
Test(int i), i = 1 //Test r(1);调用构造函数Test(int i);
/*func函数执行完毕,开始调用析构函数*/
~Test(), i = 1 //r(1)析构
~Test(), i = 0 //t析构,t是拷贝构造得来的,i=0
/*run函数执行完毕,开始消灭局部变量*/
~Test(), i = 0 //t析构
Press any key to continue...