构造函数的需求
类内部的私有成员和保护成员在外界是不可以被访问的,这也正是类的封装性的体现,所以在要为类的成员变量初始化时,这个任务就落在了类的成员函数。
class A
{
private:
int a;
int b;
public:
void init(int a,int b)
{
this->a=a;
this->b=b;
}
};
通过上述代码,我们是可以通过调用init函数对类的成员变量进行赋值,这意味着当我们创建一个对象,都要手动的调用init来初始化对象,这样的类机制是不理想的,于是就想到在创建对象时,程序自动调用构造函数进行初始化。(析构函数同样是程序自动调用的,它也是出于考虑到上述的原因)。另外考虑到类的唯一性和对象的多样性,于是就打算用类名作为构造函数的函数名。(析构函数是使用~加类名作为函数名)。
构造函数的使用
#include<iostream>
using namespace std;
class A
{
private:
int a;
int b;
public:
A(int a, int b) //类的构造函数,函数名必须和类名相同。
{
this->a = a; //构造函数和普通成员函数一样,它会有一个默认的参数,指向对象本身,就是this指针
this->b = b;
}
void print() //输出函数
{
cout << a << endl;
cout << b << endl;
}
};
int main()
{
A a(10, 11);
a.print();
system("pause");
return 0;
}
输出结果:
由上述的例子我们可以发现,构造函数是程序自己调用的,所以我们就完成了对a和b的初始化。
(构造函数是没有返回值的,也不允许有,但可以有无值的return。)
析构函数
析构函数也是特殊的函数,没有返回值,没有参数,不能随意调用,没有重载。只是在类对象生命周期结束的时候自动调用。其实我们把析构函数称为“逆构造函数”,在构造函数名前加上运算符“~”。析构函数的作用就是释放对象的所使用的内存资源。析构函数的调用顺序和构造函数的调用顺序相反。
eg:
#include<iostream>
using namespace std;
class Student
{
public :
Student()
{
cout << "I'm a student" << endl;
}
~Student()
{
cout << "I'm a student 的析构" << endl;
}
};
class Teacher
{
public:
Teacher()
{
cout << "I'm a teacher" << endl;
}
~Teacher()
{
cout << "I'm a teacher 的析构" << endl;
}
};
class School
{
private:
/*
此处以其他类的对象作为成员变量
*/
Student s;
Teacher t;
public:
School()
{
cout << "I'm a school" << endl;
}
~School()
{
cout << "I'm a school 的析构" << endl;
}
};
int main()
{
School();
system("pause");
return 0;
}
如上述代码所示,当一个类把其他类的对象作为成员变量时候,当该类创建对象自动调用该类的构造函数时,它也会同时自动调用其成员变量中类对象的构造函数,此时的构造函数调用顺序为:先调用成员变量中的类对象的构造函数,按照声明的先后顺序调用,最后调用自己的构造函数。而析构函数的调用就和构造函数的调用顺序相反。于是我们就可以得到如图的输出结果。
重载构造函数
首先我们解释一下重载的含义就是函数或方法有相同的名称,但是参数列表不同的情形。构造函数作为函数的一种,当然也可以被重载。下面给出函数重载的样例,(string就是一个类,其中就是通过构造函数的重载完成对string对象多种初始化方式)
class A
{
private:
int a;
int b;
public:
/*
构造函数的重载,定义对象时,只是通过参数的个数决定使用哪个构造函数
*/
A();
A(int a);
A(int a, int b);
};
重载构造函数为类提供了多种初始化方式,在定义对象时,程序根据所提供的参数个数来决定调用哪个构造函数。
默认的构造函数
(1):c++规定每个类必须要有一个构造函数,没有构造函数,就无法创建对象
(2):如果没定义构造函数,c++会自动的为提供一个默认的无参构造函数,但是该构造函数什么也没有干,只是拿来创建对象的。
(3):只要为一个类定义了一个构造函数(无论是有参还是无参的),系统都不会再自动的为类提供构造函数,如果需要使用无参构造函数,那就需要自己定义一个无参构造函数。
(4):创建对象和定义普通变量是一样的,在用默认的构造函数创建对象时,如果创建的是全局对象或静态对象,则对象的位模式为全0,否则,对象是随机赋值的。
类成员初始化问题
在一个类中,如果有用户自定的类对象做为其成员(如前面讲析构函数和构造函数调用顺序),那么构造函数是如何工作的了?
(1):定义该类时只需要使用无参构造函数时
#include<iostream>
using namespace std;
class Student
{
public :
Student()
{
cout << "Student" << endl;
}
};
class School
{
private:
Student s; //定义了一个类成员
public:
School()
{
cout << "School" << endl; //在调用School的构造函数之前会先调用Student的构造函数
}
};
int main()
{
School s;
system("pause");
return 0;
}
从输出结果可以看出,当我们创建了一个school对象时,它会调用构造函数来初始化其成员,其中它的一个类成员–Student s,也将调用它自己构造函数来初始化自己。
(2):当需要使用自定义构造函数(含参数)来初始化对象时
#include<iostream>
using namespace std;
class Student
{
private:
int a;
public :
Student(int a)
{
this->a = a;
cout << "Student a="<<this->a << endl;
}
};
class School
{
private:
Student s; //定义了一个类成员
public:
School(int a):s(a) //这是初始化成员变量的一种方式
{
cout << "School" << endl; //在调用School的构造函数之前会先调用Student的构造函数
}
};
int main()
{
School s(10);
system("pause");
return 0;
}
冒号语法来初始化成员变量的方式只要是为了解决:类成员调用有参构造函数来初始化自己,成员变量为常量时,只有通过这种方式才可以对其进行初始化,因为c++定义,常量是不可以被赋值的。另外一个就是引用的变量的初始化,因为引用和常量是有共同的性质的。
class School
{
private:
const int age; //声明了一个常量
int & i; //声明了一个引用
public:
School(int & a) :age(10), i(a) //初始化引用和常量
{
}
};
#include<iostream>
using namespace std;
class A
{
public :
A(int a)
{
cout << "调用: " ;
cout << "a=" << a << endl;
}
};
void fun(int a)
{
static A example(a); //声明静态对象
A example1(a+1); //声明非静态对象
cout << "fun a=" << a<<endl;
}
int main()
{
fun(10);
fun(20);
system("pause");
return 0;
}