C++构造函数
2015-07-24 16:05
344 查看
一、构造函数的特点
构造函数不能有返回类型,即使void也不行构造函数由系统自动调用,不能在程序中显示调用构造函数
构造函数的调用时机是定义对象之后的第一时间,即构造函数是对象的第一个被调用的函数
定义对象数组或用new创建动态对象时,也要调用构造函数。但定义数组对象时,必须有不需要参数的构造函数(默认构造函数或缺省参数的构造函数)
构造函数通常应定义为公有成员,因为在程序中定义对象时,要涉及构造函数的调用,尽管是由编译系统进行隐式调用,但也是在类外进行的成员函数访问
二、无参数构造函数
1.系统默认构造函数
2.重定义无参数构造函数
3.定义缺省参数的构造函数
4.无参数构造函数与缺省参数构造函数的冲突问题
三、重载构造函数
构造函数可以重载,必须有不同的函数原型。在一些情况下,可以用带缺省参数的构造函数来替代重载构造函数
如:
#include "stdafx.h" #include<iostream> using namespace std; class date { protected: int day,month, year; public: date(int d=24,int m=7 , int y=2015 ); }; int _tmain(int argc, _TCHAR* argv[]) { date date0; date date1(1); date date2(2, 7); date date3(24,7, 2015); return 0; } date::date(int d,int m, int y):day(d),month(m),year(y) { cout << year << "年" << month << "月" << day << "日" << endl; }
四、拷贝构造函数
如果要用一个已存在的类对象初始化另一个类对象,就会涉及到拷贝构造函数的调用。拷贝构造函数是一个特殊的构造函数,用于根据已存在的对象初始化一个新建对象。它的形式如下:
class X { public: X(const X&); }
发生在:X obj2=obj1时;
1.默认拷贝构造函数
默认拷贝构造函数引起的指针悬挂问题:#include "stdafx.h" #include<iostream> #include<cstring> //#pragma warning(disable:4996) using namespace std; class Person { private: char *name; int age; public: Person(const char *Name, int Age); void setAge(int Age){ age = Age; } void print(); ~Person(); }; int _tmain(int argc, _TCHAR* argv[]) { Person p1("张勇",21); Person p2 = p1; p1.setAge(22); p1.print(); p2.setAge(23); p2.print(); return 0; } Person::Person(const char*Name, int Age) { name = new char[strlen(Name) + 1]; strcpy_s(name, strlen(Name) + 1,Name); age = Age; cout << "constructor..." << endl; } void Person::print() { cout << name << "\t The age of " << name << "is" << age << endl; } Person::~Person() { cout << "destructor..." << endl; delete []name; }
该程序只调用了一次构造函数,但是调用了两次析构函数,p2的定义语句“Person p2=p1;”并没有调用构造函数。p1和p2的name成员指向了同一内存地址。
2.定义拷贝构造函数
拷贝构造函数的声明为:Person(const Person &p);#include "stdafx.h" #include<iostream> #include<cstring> //#pragma warning(disable:4996) using namespace std; class Person { private: char *name; int age; public: Person(const char *Name, int Age); Person(const Person &p); void setAge(int Age){ age = Age; } void print(); ~Person(); }; int _tmain(int argc, _TCHAR* argv[]) { Person p1("张勇",21); Person p2 = p1; p1.setAge(22); p1.print(); p2.setAge(23); p2.print(); return 0; } Person::Person(const char*Name, int Age) { name = new char[strlen(Name) + 1]; strcpy_s(name, strlen(Name) + 1,Name); age = Age; cout << "constructor..." << endl; } Person::Person(const Person &p) { name = new char[strlen(p.name) + 1]; strcpy_s(name, strlen(p.name) + 1, p.name); age = p.age; cout << "Copy constructor..." << endl; } void Person::print() { cout << name << "\t The age of " << name << "is" << age << endl; } Person::~Person() { cout << "destructor..." << age<<endl; delete []name; }
3.拷贝构造函数的应用说明
拷贝构造函数与一般构造函数相同,与类同名,无返回类型,可以重载。参数是const类型的本类对象的引用
调用拷贝构造函数的时机是用已存在的类对象初始化同类的新对象。至少一下几种情况会导致拷贝构造函数的调用。
情况1:X obj2=obj1;
情况2:X obj3(obj1);
情况3:f(X obj);
注:把情况1误会成以下情况是不正确的:
X obj2;
obj2=obj1;
这两条语句先调用无参数构造函数建立obj2,然后再用赋值语句将obj1赋值给obj2,并不会调用拷贝构造函数。但情况1不会调用赋值语句,而是通过拷贝构造函数初始化建立的obj2对象。
在多数情况下,默认拷贝构造函数能够完成对象的复制工作,但当类具有指针类型数据成员时,默认拷贝构造函数就可能产生指针悬挂问题,需显式提供拷贝构造函数。
对拷贝构造函数的调用常在类的外部进行,应将它指定为public成员。
五、构造函数与初始化列表
形式:构造函数名(参数列表):成员1(参数1),成员2(参数2),…
{
}
说明:
构造函数初始化列表中的成员初始化次序与它们在类中的声明次序相同,与初始化列表中的次序无关。
构造函数初始化列表先于构造函数体中的语句执行。
下列类成员必须采用初始化列表进行初始化:常量成员,引用成员,类对象成员,派生类构造函数对基类构造函数的调用。
六、构造函数与派生类
如果基类的所有构造函数都有参数,那么派生类构造函数就必须显示地调用其中的某一个,以完成对基类对象的初始化。 在创建派生类对象时,派生类的构造函数除了要负责本类成员的初始化外,还要调用基类成员和成员对象的构造函数,并向它们传递参数,以完成基类子对象和成员对象的建立和初始化。 派生类只能采用初始化列表的方式向基类或成员对象的构造函数传递参数,形式如下: 派生类构造函数(参数列表):基类构造函数函数名(参数列表),成员对象名1(参数列表),...{//...}
以初始化方式调用基类构造函数是C++派生类调用基类构造函数的唯一方式,是派生类向基类构造函数传递参数的唯一方法,初始化列表中的构造函数先于派生类构造函数体执行。
调用次序:
基类构造函数→成员对象构造函数→派生类构造函数体
1.派生类可以不定义构造函数的情况:
基类没有定义任何构造函数基类具有默认参数的构造函数
基类具有无参数构造函数
在这些情况下,派生类可以不向基类传递参数,甚至不需要显式定义构造函数(如果派生类没有成员需要初始化)。
2.派生类必须定义构造函数的情况:
当基类或成员对象所属类只含有带参数的构造函数时,即使派生类本身没有数据成员要初始化,它也必须定义构造函数,并以构造函数初始化列表的方式向基类和成员对象的构造函数传递参数,以实现基类子对象和成员对象的初始化。3.派生类的构造函数只负责直接基类的初始化:
如果派生类的基类同时也是另外一个类的派生类,则每个派生类只负责它的直接基类的构造函数的调用。这条规则表明,当派生类的直接基类只有带参数的构造函数,但没有默认构造函数时(包括默认参数和无参数构造函数),它必须在构造函数的初始化列表中调用其直接基类的构造函数,并向其基类的构造函数传递参数,以实现派生类对象中的基类子对象的初始化。
例外:当派生类存在虚基类时,所有虚基类都是由最后的派生类负责初始化。
相关文章推荐
- sizeof用法的总结
- 其实你应该这样学C++(1)
- C++交换两个数的值
- c++基础i
- 头文件 string.h cstring string 区别
- C++函数间数据传递方式
- C++函数间数据传递方式
- 头文件 string.h cstring string 区别
- cout运算顺序(←)
- c++基础知识整理二
- C++基础知识学习整理一
- 一道360面试题引发的思考
- Up and Running with C++
- 单向链表创建(一)
- 复数运算类
- 《零起点学通C++》 系列笔记 -- 第15章 链表
- C语言指针、数组的使用和内存地址
- 初学C++,第一个程序和C++基础知识
- C++读入文本文件的编码方式以及影响
- c++初学可能犯得两个错误