版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
1.对象的构造(上)
1.1 问题
- 对象中成员变量的初始值是多少?
-
#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\n", gt.getI());
-
printf(
"gt.j = %d\n", gt.getJ());
-
-
Test t1;
// 栈
-
-
printf(
"t1.i = %d\n", t1.getI());
-
printf(
"t1.j = %d\n", t1.getJ());
-
-
Test* pt =
new Test;
// 堆
-
-
printf(
"pt->i = %d\n", pt->getI());
-
printf(
"pt->j = %d\n", pt->getJ());
-
-
delete pt;
-
-
getchar();
-
-
return
0;
-
}
- 运行结果
1.2 对象的初始化
- 从程序设计的角度,对象只是变量,因此:
- 在栈上创建对象时,成员变量初始为随机值
- 在堆上创建对象时,成员变量初始为随机值 (gcc会为其赋0)
- 在静态存储区创建对象时,成员变量初始为0值
- 问题: 程序中如何对—个对象进行初始化?
- 一般而言,对象都需要—个确定的初始状态
- 解决方案 :
- 在类中提供—个public的initialize函数
- 对象创建后立即调用initialize函数进行初始化
-
#include <stdio.h>
-
-
class Test
-
{
-
private:
-
int i;
-
int j;
-
-
public:
-
int getI()
-
{
-
return i;
-
}
-
-
int getJ()
-
{
-
return j;
-
}
-
-
void initialize()
-
{
-
i =
1;
-
j =
2;
-
}
-
};
-
-
Test gt;
-
-
int main()
-
{
-
gt.initialize();
-
-
printf(
"gt.i = %d\n", gt.getI());
-
printf(
"gt.j = %d\n", gt.getJ());
-
-
Test t1;
-
-
//t1.initialize(); //若没有立即调用
-
-
printf(
"t1.i = %d\n", t1.getI());
-
printf(
"t1.j = %d\n", t1.getJ());
-
-
t1.initialize();
//在此处调用
-
-
Test* pt =
new Test;
-
pt->initialize();
-
-
printf(
"t1.i = %d\n", t1.getI());
-
printf(
"t1.j = %d\n", t1.getJ());
-
printf(
"pt->i = %d\n", pt->getI());
-
printf(
"pt->j = %d\n", pt->getJ());
-
-
delete pt;
-
-
getchar();
-
-
return
0;
-
}
- 运行结果
- 存在的问题
<ul><li>initialize只是—个普通函数,必须显示调用 </li> <li>如果未调用initialize函数,运行结果是不确定的 </li> </ul></li>
1.3 构造函数
- C++中可以定义与类名相同的特殊成员函数 ,这种特殊的成员函数叫做构造函数
- 构造没有任何返回类型的声明
- 构造函数在对象定义时自动被调用
-
#include <stdio.h>
-
-
class Test
-
{
-
private:
-
int i;
-
int j;
-
-
public:
-
int getI()
-
{
-
return i;
-
}
-
-
int getJ()
-
{
-
return j;
-
}
-
-
Test()
-
{
-
printf(
"Test() Begin\n");
-
-
i =
1;
-
j =
2;
-
-
printf(
"Test() End\n");
-
}
-
};
-
-
Test gt;
-
-
int main()
-
{
-
printf(
"gt.i = %d\n", gt.getI());
-
printf(
"gt.j = %d\n", gt.getJ());
-
-
Test t1;
-
-
printf(
"t1.i = %d\n", t1.getI());
-
printf(
"t1.j = %d\n", t1.getJ());
-
-
Test* pt =
new Test;
-
-
printf(
"pt->i = %d\n", pt->getI());
-
printf(
"pt->j = %d\n", pt->getJ());
-
-
delete pt;
-
-
getchar();
-
-
return
0;
-
}
- 运行结果
1.4 小结
- 每个对象在使用之前都应该初始化
- 类的构造函数用于对象的初始化
- 构造函数与类同名并且没有返回值
- 构造函数在对象定义时自动被调用
2.对象的构造(中)
2.1 对象定义和对象声明不同
- 对象定义-申请对象的空间并调用构造函数
- 对象声明-告诉编译器存在这样—个对象
-
Test t;
//定义对象并调用构造函数
-
-
int main()
-
{
-
-
extern Test t;
//告诉编译器存在名为t的Test对象
-
-
return
0;
-
}
2.2 带参数的构造函数
- 构造函数可以根据需要定义参数
-
#include <stdio.h>
-
-
class Test
-
{
-
public:
-
Test()
-
{
-
printf(
"Test()\n");
-
}
-
-
Test(
int v)
-
{
-
printf(
"Tset(int v), v = %d\n", v);
-
}
-
-
};
-
-
int main()
-
{
-
Test t;
// 调用 Test()
-
Test t1(1);
// 调用Test(int v)
-
Test t2 =
2;
// 调用Test(int v)
-
-
int i(100);
// 赋值, 注意C++中对象初始化和赋值差距
-
-
printf(
"i = %d\n", i);
-
-
getchar();
-
-
return
0;
-
}
- 运行结果:
2.3 构造函数的调用
- 一般情况下,构造函数在对象定义时被自动调用
- 一些特殊情况下,需要手工调用构造函数
-
#include <stdio.h>
-
-
class Test
-
{
-
private:
-
int i;
-
int j;
-
int k;
-
-
/*这种与类名相同的成员函数叫做构造函数
-
构造函数在定义时可以有参数,但是没有任何返回类型的声明
-
*/
-
public:
-
Test(
int v)
-
{
-
i = v;
-
j = v;
-
k = v;
-
}
-
-
void print()
-
{
-
printf(
"i = %d, j = %d, k = %d\n", i, j, k);
-
}
-
};
-
-
int main()
-
{
-
Test t1(4);
// 自动调用构造函数
-
Test t2 =
5;
// 自动调用构造函数
-
Test t3 = Test(
6);
// 主动调用构造函数
-
-
t1.print();
-
t2.print();
-
t3.print();
-
-
Test tA[
3] = {Test(
4), Test(
5), Test(
6)};
// 主动调用构造函数
-
-
for(
int i =
0; i <
3; i++)
-
{
-
tA[i].print();
-
}
-
-
printf(
"Press any key to continue...");
-
-
getchar();
-
-
return
0;
-
}
- 运行结果
2.4 成员函数的重载
- 类的成员函数和普通函数一样可以进行重载,并遵守相同的重载规则
-
#include <stdio.h>
-
-
class Test
-
{
-
private:
-
int i;
-
int j;
-
int k;
-
-
public:
-
Test()
-
{
-
i =
0;
-
j =
0;
-
k =
0;
-
}
-
-
Test(
int v)
-
{
-
i = v;
-
j = v;
-
k = v;
-
}
-
-
void print()
-
{
-
printf(
"i = %d, j = %d, k = %d\n", i, j, k);
-
}
-
-
void print(int v)
-
{
-
printf(
"v = %d\n", v);
-
}
-
};
-
-
int main()
-
{
-
Test t1(1);
-
Test t2 =
2;
-
Test t3 = Test(
3);
-
Test t4;
-
Test t5;
-
-
t5.print(
5);
-
t4.print();
-
t1.print();
-
t2.print();
-
t3.print();
-
-
-
printf(
"\n");
-
-
Test tA[
3] = { Test(
5),
6,
7 };
-
-
for(
int i =
0; i <
3; i++)
-
{
-
tA[i].print();
-
}
-
-
printf(
"Press any key to continue...");
-
-
getchar();
-
-
return
0;
-
}
- 运行结果
2.5 小结
- 构造函数可以根据需要定义参数
- 构造函数之间可以存在重载关系
- 构造函数遵循C++中重载函数的规则
- 对象定义时会触发构造函数的调用
- 在—些情况下可以手动调用构造函数
3.对象的构造(下)
3.1 两个特殊的构造函数
- 无参构造函数:没有参数的构造函数
<ul><li>无参构造函数 :当类中没有定义构造函数时,编译器默认提供—个无参构造函数,并且其函数体为空 </li> </ul></li> <li><span style="color:#f33b45;"><strong>拷贝构造函数:参数为const class_name&的构造函数</strong></span> <ul><li>当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制</li> </ul></li>
-
#include <stdio.h>
-
-
class Test
-
{
-
private:
-
int i;
-
int j;
-
int k;
-
-
public:
-
Test()
// 当提供拷贝构造而不提供无参构造,编译器报错
-
{
-
-
}
-
-
Test(
const Test &t)
// 不写默认提供
-
{
-
i = t.i;
-
j = t.j;
-
k = t.k;
-
}
-
-
void print()
-
{
-
printf(
"i = %d, j = %d, k = %d\n", i, j, k);
-
}
-
};
-
-
int main()
-
{
-
Test t1;
-
Test t2 = t1;
-
-
t1.print();
-
t2.print();
-
-
printf(
"Press any key to continue...");
-
-
getchar();
-
-
return
0;
-
}
- 运行结果:
- 拷贝构造函数的参数不能用值传递,只能用引用。如果用值传递,在函数运行时,函数内部会产生一个新的副本(实参副本)接收实参的值(形参变量的值是实参变量的副本),从而又要调用拷贝构造函数,造成死循环。
- 如果类中一个构造函数都没有写,则编译器自动提供无参构造函数和拷贝构造函数。但如果已经存在某种构造函数,但编译器不会提供无参构造函数,但任然会提供拷贝构造函数。
3.2 拷贝构造函数
- 拷贝构造函数的意义
- 兼容C语言的初始化方式
- 初始化行为能够符合预期的逻辑
- 浅拷贝
- 拷贝后对象的物理状态相同
- 深拷贝
- 拷贝后对象的逻辑状态相同
- 编译器提供的拷贝构造函数只进行浅拷贝!
-
#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;
-
}
-
-
void free()
-
{
-
delete p;
-
}
-
};
-
-
int main()
-
{
-
Test t1(3);
-
Test t2 = t1;
-
-
printf(
"t1.i = %d, t1.j = %d, t1.p = %p\n", t1.getI(), t1.getJ(), t1.getP());
-
printf(
"t2.i = %d, t2.j = %d, t2.p = %p\n", t2.getI(), t2.getJ(), t2.getP());
-
-
//t1.free();
-
//t2.free();
-
-
getchar();
-
-
return
0;
-
}
- 运行结果:
- 调用了默认拷贝构造函数,成员变量值一样,但当释放堆空间 t1.free(); t2.free(); 两次释放了堆空间的内存
- 解决方案:增加拷贝构造函数,实现深拷贝
-
#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 = %p\n", t1.getI(), t1.getJ(), t1.getP());
-
printf(
"t1.i = %d, t1.j = %d, *t1.p = %d\n", t1.getI(), t1.getJ(), *t1.getP());
-
-
printf(
"t2.i = %d, t2.j = %d, t2.p = %p\n", t2.getI(), t2.getJ(), t2.getP());
-
printf(
"t2.i = %d, t2.j = %d, *t2.p = %d\n", t2.getI(), t2.getJ(), *t2.getP());
-
-
t1.
free();
-
t2.
free();
-
-
getchar();
-
-
return
0;
-
}
- 运行结果:
- 什么时候需要进行深拷贝?
- 对象中有成员指代了系统中的资源
- 成员指向了动态内存空间
- 成员打开了外存中的文件
- 成员使用了系统中的网络端口
.......
-
3.3 数组类的创建
- 需求:开发—个数组类解决原生数组的安全性问题
- 提供函数获取数组长度
- 提供函数获取数组元素
- 提供函数设置数组元素
- 编程实验
- 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);
// 获取数组元素
-
void destory();
-
};
-
-
#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];
-
}
-
-
void Array::destory()
-
{
-
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));
-
}
-
-
printf(
"\n");
-
-
Array a2 = a1;
// // 自动调用拷贝构造函数
-
-
for(
int i =
0; i < a2.length(); i++)
-
{
-
printf(
"Element %d: %d\n", i, a2.getData(i));
-
}
-
-
a1.destory();
-
a2.destory();
-
-
printf(
"Press any key to continue...");
-
-
getchar();
-
-
return
0;
-
}
- 运行结果
3.4 小结
- C++编译器会默认提供构造函数
- 无参构造函数用于定义对象的默认初始状态
- 拷贝构造函数在创建对象时拷贝对象的状态
- 对象的拷贝有浅拷贝和深拷贝两种方式
- 浅拷贝使得对象的物理状态相同
- 深拷贝使得对象的逻辑状态相同