1、构造函数
(1)每个类都要定义它自己的构造函数和析构函数,是类的成员函数。
特点:名称与类名相同;没有返回值;一定是共有函数,可以直接访问类内所有成员函数;可以带默认形参,可以重载;
class clock { public: // 无参构造函数 clock() { cout << "无参构造函数" << endl; } // 有参构造函数 clock(int h, int m , int s) { hour = h; minute = m; second = s; cout << "有参构造函数" << endl; } //// 有默认参数值的有参构造函数 //clock(int h=8, int m=20, int s=20) //{ // hour = h; // minute = m; // second = s; // cout << "有默认参数值的有参构造函数" << endl; //} //拷贝构造函数完成,用一个对象初始化另一个对象 clock(const clock &t) // const 加不加都可以 { cout << "复制构造函数" << endl; } private: int hour; int minute; int second; }; void main() { clock myclock; // 调用无参构造函数,不能加括号 //clock myclock_1();// 调用有参构造函数,此时会出错,因为没有传参数 clock myclock_2(8,20,20);// 有参构造函数,必须要写上参数 clock myclock_3(myclock_2);// 调用复制构造函数
clock myclock_3 = myclock_2;// 同样调用copy构造函数
system("pause"); }
(2)注意:
(1)类中没有定义任何构造函数时,才使用默认构造函数,只要定义了,就不会存在默认构造函数;
(2)不能同时出现,无参构造函数和带有全部默认参数值的构造函数;二者只能出现一个;
(3)复制构造函数,使用一个已存在的对象,初始化一个新的同类对象。如果未定义,系统将会自动生成;但是,如果申请动态空间(堆空间),则必须定义。
(4)构造函数中调用构造函数,是危险的行为。(会因为调用匿名对象,而直接析构掉)
2、对象数组
解释为什么需要构造函数,在定义对象数组时,自动初始化。
class student { public: student(int, char*); student(); ~student(); void set(int i, char* c); void printstu() { cout << "id: " << id << " name: " << name << setw(5) << endl; } private: int id; string name; }; student::student() { cout << "默认无参构造函数" << endl; } student::student(int i, char* c) { id = i; name = c; cout << "有参构造函数" << endl; } student::~student() { cout << "析构函数" << endl; } void student::set(int i, char* c) { id = i; name = c; } void main() { // 对象数组 student stu[5] = { student(1,"li"), student(2,"wang") }; stu[2].set(3, "zhao"); system("pause"); }
定义两个构造函数,此时定义对象数组时,可以不用初始化。
构造函数在定义对象时调用,析构函数在程序结束时调用,而且,析构顺序与构造顺序相反。
3、复制构造函数
复制构造函数,也是构造函数。只在初始化时调用,如果定义对象后赋值,比如,t1=t2,则只是运算符重载,没有调用构造函数。
student::student(student &s) { cout << "复制构造函数" << endl; id = s.id; // 使用字符指针定义的变量,需要定义复制构造函数,申请空间。 // 也需要定义 构造函数 和 析构函数 name = new char[strlen(s.name) + 1]; if (name != 0) strcpy_s(name, strlen(s.name) + 1, s.name); }
调用:有四种方法。
// 对象数组 student stu[5] = { student(1,"li"), student(2,"wang") }; stu[2].set(3, "zhao");
1、 student stucopy(stu[0]);
2、 student stucopy_1 = stu[0];
还可以用函数调用:
void f(student p) { // 用于类对象的复制。 cout << "此处要调用复制构造函数" << endl; } student stu; stu.set(3, "zhao"); f(stu);
还有一种情况:就是函数返回值是一个类对象,返回的是一个新的匿名对象,此处要调用复制构造函数。
student g() { student stu1(1, "abc"); return stu1; // 用于类对象的复制。返回的匿名对象的复制,因为 stu1 是局部变量,函数结束时要销毁。 cout << "此处要调用复制构造函数" << endl; }
student s = g();//此处只是换了名字,没有调用复制构造函数了
如果匿名对象,初始化另一个同类型的对象,则匿名对象转成有名对象,不调用复制构造函数;
如果匿名对象赋值给另一个对象,则匿名对象马上被析构;
4、匿名对象的声明周期
int run3() { cout << "run3 start...." << endl; B(1, 2); //执行此步,匿名对象调用构造函数后,马上调用析构函数(因为没有东西接) //此处,匿名函数被 b 接到了,就只调用了构造函数,匿名对象转成有名对象 // 该有名对象是局部变量,在函数结束返回时析构(也就是 return 处)。 B b = B(1, 2); cout << "run3 end...." << endl; return 0; }