对象的构造与析构(一)
2019-09-17 09:56:08来源:博客园 阅读 ()
对象的构造与析构(一)
目录
- 1. 构造函数的定义
- 2. 构造函数的重载
- 3. 两个特殊的构造函数
- 4. 初始化列表的使用
1. 构造函数的定义
从程序设计的角度,类的对象只是变量,在栈上和堆上创建对象时,成员变量初始为随机值;创建全局对象时,成员变量初始为0值。
C++中可以定义与类名相同的特殊成员函数,叫做构造函数
- 构造函数没有任何返回类型
- 类的构造函数在对象定义时自动被调用,进行对象的初始化工作
- 对象的定义和声明是不同的
Test t1; //定义对象,为对象分配内存空间,并调用构造函数
extern Test t2; //声明对象,告诉编译器存在这样一个对象
2. 构造函数的重载
- 一个类中可以根据需要定义多个重载的构造函数
- 构造函数的重载遵循C++重载的规则
#include <stdio.h>
class Test
{
public:
Test()
{
printf("Test()\n");
}
Test(int v)
{
printf("Test(int v), v = %d\n", v);
}
Test(int v1, int v2)
{
printf("Test(int v1, int v2), v1 = %d, v2 = %d\n", v1, v2);
}
};
int main()
{
Test t; //调用 Test()
Test t1(1); //调用 Test(int v)
Test t2 = 2; //调用 Test(int v),只有一个参数时可以用赋值符号
Test t3(1, 2); //调用Test(int v1, int v2)
//Test t4 = ?; //多个参数时,不能用赋值符号
int i(100);
printf("i = %d\n", i);
return 0;
}
对于多个重载的构造函数,在定义对象时C++会自动匹配选择使用哪一个,但在一些特殊情况下,可能需要显式调用构造函数,比如下面的例子。
#include <stdio.h>
class Test
{
private:
int value;
public:
Test()
{
value = 0;
}
Test(int v)
{
value = v;
}
int getValue()
{
return value;
}
};
int main()
{
Test t1[3]; //自动调用构造函数,所有对象默认用Test()进行初始化
Test t2[3] = {Test(), Test(1), Test(2)}; //手动调用构造函数,对象使用Test(int v)进行初始化
for (int i = 0; i < 3; i++)
{
printf("t1[%d].value = %d\n", i, t1[i].getValue());
}
for (int i = 0; i < 3; i++)
{
printf("t2[%d].value = %d\n", i, t2[i].getValue());
}
return 0;
}
3. 两个特殊的构造函数
- 有两个特殊的构造函数:无参构造函数和拷贝构造函数(参数为
const ClassName &
的构造函数) - 当类中没有定义任何构造函数时,编译器默认提供一个函数体为空的无参构造函数
- 当类中没有定义拷贝构造函数时,编译器默认提供一个浅拷贝的拷贝构造函数,进行简单的成员变量值的复制
#include <stdio.h>
class Test
{
private:
int i;
int j;
public:
Test()
{
i = 1;
j = 2;
}
Test(const Test &obj)
{
i = obj.i;
j = obj.j;
}
int getI()
{
return i;
}
int getJ()
{
return j;
}
};
int main()
{
Test t1; //调用无参构造函数
Test t2 = t1; //调用拷贝构造函数
printf("t1.i = %d, t1.j = %d\n", t1.getI(), t1.getJ());
printf("t2.i = %d, t2.j = %d\n", t2.getI(), t2.getJ());
return 0;
}
把代码第9-18行两个构造函数注释掉,看以看到运行结果和上面一致,只不过i和j的值变为了随机值。
拷贝构造函数有深拷贝和浅拷贝之分:
- 浅拷贝后对象的物理状态相同,深拷贝后对象的逻辑状态相同
- 编译器提供的拷贝构造函数只进行浅拷贝
当对象中有成员指代了系统中的资源时,就需要进行深拷贝,比如:
- 成员指向了堆空间内存
- 成员打开了系统中的文件
- 成员使用了系统中的网络端口
- ......
作为程序设计的一般性原则,只要自定义拷贝构造函数,必然需要实现深拷贝!!!
#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(const Test &t)
{
i = t.i;
j = t.j;
p = new int;
*p = *t.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 = %p, *t1.p = %d\n", t1.getI(), t1.getJ(), t1.getP(), *t1.getP());
printf("t2.i = %d, t2.j = %d, t2.p = %p, *t2.p = %d\n", t2.getI(), t2.getJ(), t2.getP(), *t2.getP());
return 0;
}
4. 初始化列表的使用
C++提供了初始化列表对成员变量进行初始化,语法规则为:
ClassName :: ClassName() :
m1(v1), m2(v1, v2), m3(v3)
{
// some other initialize operation
}
关于初始化列表的使用,有以下几条规则:
- const成员变量只能通过初始化列表初始化
- 如果成员变量中有其他类的对象,那么这些对象也只能通过初始化列表初始化
- 成员变量的初始化顺序与成员在类中的声明顺序相同,与初始化列表中的位置无关
- 初始化列表先于构造函数的函数体执行
#include <stdio.h>
class Value
{
private:
int mi;
public:
Value(int i)
{
printf("Value()::i = %d\n", i);
mi = i;
}
int getI()
{
return mi;
}
};
class Test
{
private:
const int ci;
Value m2;
Value m3;
Value m1;
public:
Test() : m1(1), m2(2), m3(3), ci(100)
{
printf("Test::ci = %d\n", ci);
}
int getCI()
{
return ci;
}
int setCI(int v)
{
int *p = const_cast<int *>(&ci);
*p = v;
}
};
int main()
{
Test t;
printf("t.ci = %d\n", t.getCI());
t.setCI(10);
printf("t.ci = %d\n", t.getCI());
return 0;
}
原文链接:https://www.cnblogs.com/songhe364826110/p/11529935.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- C++ 对象的初始化和赋值 2020-06-03
- C++ 析构函数 2020-06-03
- C++ 构造函数 2020-06-03
- 定位new表达式与显式调用析构函数 2020-04-20
- 使用错误代码对象进行C++错误处理 2020-04-10
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash