一、构造函数:
c++类所定义的对象初始化方式:构造函数:
实现方式:在类中定义构造函数,
形式:
类名(){对类中成员变量的初始化内容}
其中:
(1)构造函数可以既可以给予形参也可以无形参。例:**
class A{
public:
A(){a = 8;};
void show(){cout<<a<<endl;};
private:
int a;
};
//或者可以这样:
class A{
public:
A(int b){a = b;};
void show(){cout<<a<<endl;};
private:
int a;
};
(2)构造函数可以允许函数重载:
class A{
public:
A(){a = 8;};
A(int b){a = b;};
void show(){cout<<a<<endl;};
private:
int a;
};
注意:当对c++构造函数进行重载时,实例化对象的时候若传入什么类型的参数,则会自动找到对应形参的构造函数进行对象的初始化。
(3)默认构造函数(没有参数的构造函数或带有默认值形参的构造函数):
1、无用默认构造函数:
两种:
(1)在常规的类中,当不人工给予它一个构造函数时,编译器将会自动为该类生成一个默认构造函数,且该构造函数不做任何事情,所以称为无用默认构造函数,作用是保证程序运行不会出错:
class A{
public:
// 编译器自动A一个默认构造函数A(){};
void show(){cout<<"hello world!"<<endl;};
};
(2)人工定义一个构造函数且不传入任何参数,也不实现任何功能,我们也称之为默认构造函数:
class A{
public:
A(){};
void show(){cout<<"hello world!"<<endl;};
};
2.有用构造函数:
(1)第一种情况:若一个类A中包含一个带有默认构造函数的成员类对象B,而未人工给该类定义一个能够实现成员类对象B中的默认构造函数的A的构造函数,则编译器会自动给类A一个构造函数去实现类B中的默认构造函数。
class A{
public:
A(){};
void show(){cout<<"hello world!"<<endl;};
};
class B{
public:
//这里由于无人工定义构造函数,编译器会自动给予B一个构造函数
//相当于:
// B(){a.A()};
A a; //类B包含一个带有默认构造函数的成员类对象a。
void show(){cout<<a<<endl;};
private:
int a;
};
注意:成员类对象a必须有默认构造函数,且若不是以默认构造函数产生的成员,也会报错,比如A a(1);若有多个类成员变量,按照声明顺序去调用构造函数。
(2)第二种情况:父类中有自定义的无参构造函数,而子类中没有任何自定义构造函数,那么子类会由编译器自动定义一个默认构造函数以实现父类的默认构造。如:
class A{
public:
A(){cout<<"B调用了我哦"<<endl;};
void show(){cout<<"hello world!"<<endl;};
};
class B:public A{
public:
void show1(){cout<<"lala"<<endl;};
private:
int a;
};
int main(){
B b;
b.show1();
return 0;
}
结果为
这说明类B中编译器自动生成了一个默认构造函数,去实现父类A的默认构造函数。
(4)特殊初始化:
当对带有默认 参数 的构造函数的类进行对象的初始化时,可以用类似于默认参数初始化类的对象的方式来进行初始化。其特性是将该构造函数传值0进去进行构造,如:
class A{
public:
A(int a = 10){b = a;};
void show(){cout<<"b="<<b<<endl;};
private:
int b;
};
int main(){
A *a = new A;
a.show();
}
输出值 b=10,说明它调用了带默认参数的构造函数
(5)构造函数初始化列表:
形式:
class A{
public:
A():a(5),b(10){}
private:
int a;
int b;
};
class B{
public:
B(){a = 5;b = 10;}
pirvate:
int a;
int b;
};
构造函数初始化列表即以一个冒号开始,接着以逗号分隔的数据成员列表,在每个成员后跟一个括号初始化式。
什么时候用到:
一,需要初始化的数据成员是类对象,而且该类对象并没有默认构造函数的情况。
二,需要初始化const数据成员。
三,需要初始化引用数据成员。
四、出于效率的考虑。
五、注意对基类成员和新增成员对象的初始化必须在成员初始化列表中进行。
(6)拷贝构造函数:
1.拷贝构造函数是一种特殊的构造函数,在创建对象时,使用同一类中之前创建的对象来初始化新创建的对象。
常见形式:
classname (const classname &obj) {
// 构造函数的主体
}
2.一般而言,如果在类中没有定义拷贝构造函数,编译器会自行定义一个。但如果类中有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数,避免对象复制的时候两个指针指向了同一个空间。
例子:
class A
{
public:
A(){a = NULL;}
A(const A &b){a = new int; *a = *b.a;}
~A(){delete a;}
void init(int n){a = new int; *a = n;}
void show(){cout<<"a="<<*a<<endl; cout<<"&a="<<a<<endl;}
private:
int *a;
};
int main(){
A a;
a.init(6);
A b(a);
b.show();
a.show();
return 0;
}
//输出结果:
a=6
&a=00032D18
a=6
&a=00032CE0
可以看出两个对象中的a虽然值相同了,但所指的空间变了,这就是所需要自定义拷贝构造函数的情况。
3.拷贝构造函数的调用情况:
(1)用类的一个对象去初始化另一个对象的时候:假设此时有个类A
A a;
A b(a);//将调用拷贝构造函数
A c = a; //将调用拷贝构造函数
A d,e;
d = e; //不会调用拷贝构造函数
(2)当函数的参数是类的对象时,就是值传递的时候,如果是引用传递则不会调用
class A{
public:
A(){};//构造函数
A(A&a)//拷贝构造函数
{
cout<<"拷贝构造函数被调用"<<endl;
}
};
void fun(A a)
{
}
int main(){
A a;
fun(a); //此时调用拷贝构造函数,若形参会对象的引用则不会
return 0;
}
(3)当函数的返回值是类的对象或者引用的时候
class A{
public:
int v;
A(int i){v = i;};//构造函数
A(A&a)//拷贝构造函数
{
cout<<"拷贝构造函数被调用"<<endl;
}
};
A a(4);
A fun()
{
return a;
}
int main(){
cout<<fun().v<<endl;; //此时调用拷贝构造函数,若形参会对象的引用则不会
return 0;
}
最后注意一点:构造函数不允许有虚构造函数。
二、析构函数:
定义格式:~类名(){}
注意:
1.不允许加任何参数。
2.如果没有自定义的析构函数,系统会自动生成。
3.在对象销毁时自动调用。
4.析构函数没有返回值,没有参数不能重载。
5.允许虚析构。
作用:释放内存,举个例子:
class A{
public:
A(){a=new int;}
~A(){delete a;}
private:
int *a;
}
//当类结束调用时,自动释放a的内存。