在C++中,对于一个空的类,编译器一般默认它会有4个成员函数:构造函数,析构函数,复制构造函数和赋值函数。对于初学者而言,构造函数一定是要知道的:它的功能就是在创建一个新的对象的时候给数据成员赋初值,也就是给对象做初始化。析构函数则是用来释放一个对象,在对象删除之前用它来做一些内存释放等的清理工作,与构造函数的功能相反。
关于类的构造函数和析构函数的典型应用:在构造函数中用new来为指针成员开辟一个独立的动态内存空间,而在析构函数中用delete来释放它。
关于C++中对一个对象的初始化可以是下面这样:
在上面的语句中,B语句将o2作为o3的初值,和o2一样,o3的初始化形式要调用相应的构造函数,但是此时找不到与之匹配的构造函数,因为ClassName类中没有哪个构造函数的形参是ClassName类的对象,由此引出ClassName所隐含的一个特殊的默认构造函数,其原型是:
这种默认的构造函数就称作默认拷贝(复制)构造函数。不过这仅仅是将内存空间的内容做了拷贝,这种拷贝方式称为浅拷贝。而对于数据成员有指针类型的类来说,默认的拷贝构造函数无法完成对其内容的拷贝,此时的解决办法就是必须自己定义一个拷贝构造函数,然后在进行数值拷贝之前为指针类型的数据成员重新开辟一个独立的内存空间,这种还需要另开辟新的内存空间的拷贝方式称作是深拷贝。(关于深拷贝和浅拷贝后面深入学习。。。)
拷贝构造函数除了要保持和普通构造函数的声明和实现规则之外,还要按照下列格式定义:
所以其实可以这么总结拷贝构造函数:拷贝构造函数的格式就是带有参数的构造函数。实际上,拷贝操作的实质就是类的对象空间的引用,所以呢,在C++的规定中就说拷贝构造函数的参数个数可以是1个或者多个,但是第一个参数必须是类的引用对象,这个参数的形式可以是“
类名 &对象名”或者“
const 类名 &对象名”的形式,这里的类名就是该拷贝构造函数所在的类的类名。
说明:一旦在类中定义了拷贝构造函数,那么隐式的默认拷贝构造函数和隐式的默认构造函数就不再有效。
测试 实例:
关于类的构造函数和析构函数的典型应用:在构造函数中用new来为指针成员开辟一个独立的动态内存空间,而在析构函数中用delete来释放它。
关于C++中对一个对象的初始化可以是下面这样:
点击(此处)折叠或打开
-
//<类名><对象名1>(<对象名2>)
- ClassName o1("object"); //A:通过构造函数设定初始值
- ClassName o3(o2); //B:通过指定的对象设定初始值
点击(此处)折叠或打开
- ClassName(const ClassName &);
拷贝构造函数除了要保持和普通构造函数的声明和实现规则之外,还要按照下列格式定义:
点击(此处)折叠或打开
- <类名>(参数表)
- {……}
说明:一旦在类中定义了拷贝构造函数,那么隐式的默认拷贝构造函数和隐式的默认构造函数就不再有效。
测试 实例:
点击(此处)折叠或打开
- //MyString.h
- #include <cstddef>//定义宏NUL的头文件
- #include <QObject>
- #include <iostream>
- //#include <cstdio>
- #include <string.h>
-
- using namespace std;
-
- class String
- {
- public:
- String(const char *str = NULL);//普通构造函数
- String(const String &other);//拷贝构造函数
- ~String(void);//析构函数
- String & operator = (const String &other);//赋值函数
- public:
- char *m_data;//用来保存字符串
- };
-
- #endif // STRING_H
点击(此处)折叠或打开
- //MyString.cpp
- #include "MyString.h"
-
- /*
- *普通构造函数
- *构造函数首先根据一个字符串常量创建一个String对象。
- *这个构造函数首先分配了足够的内存,然后把这个字符串常量复制到这块内存
- */
- String::String(const char *str)
- {
- if (str == NULL) {
- m_data = new char[1];
- *m_data = '\0';
- } else {
- int length = strlen(str);
- m_data = new char[length + 1];
- strcpy(m_data, str);
- }
- }
- /*
- *拷贝构造函数
- *所有需要分配系统资源的用户定义类型都需要一个拷贝构造函数
- *它可以在函数调用中以传值得方式传递一个String类型的参数
- *并且在当一个函数以值得形式返回String对象时实现“返回时复制”
- */
- String::String(const String &other)
- {
- int length = strlen(other.m_data);
- m_data = new char[length + 1];
- strcpy(m_data, other.m_data);
- }
- /*
- *定义析构函数是为了方式内存泄露,当一个String对象超出
- *它的作用域时,析构函数就会释放它所占用的内存
- */
- String::~String(void)
- {
- delete[] m_data;//m_data是内部数据类型,也可以写作delete m_data
- }
- /*
- *赋值函数实现字符串的传值活动
- */
- String & String::operator = (const String &other)
- {
- if (this == &other)//检查自赋值
- return *this;
-
- delete[] m_data;
-
- int length = strlen(other.m_data);//分配新的内存资源并复制其内容
- m_data = new char[length + 1];
- strcpy(m_data, other.m_data);
-
- return *this;//返回本对象的引用
- }
点击(此处)折叠或打开
- //main.cpp
- #include <QApplication>
- #include "MyString.h"
- #include <QDebug>
-
- int main(int argc, char *argv[])
- {
- QApplication a(argc, argv);
-
- String MyString("My first String test!!!");
- String MyString2, MyString3 = MyString;
- MyString2 = MyString;
-
- qDebug() << "MyString:" << MyString.m_data << "MyString2:" << MyString2.m_data
- << "MyString3:" << MyString3.m_data;
-
- return a.exec();
- }