1、作用:创建对象时,给对象的属性进行初始化
2、特点
(1)构造函数与类同名
(2)如果没有显式给出构造函数,编译器会给出默认的构造函数(参数为空,并且函数体也为空);如果给出任意的构造函数,系统默认的构造函数就不存在了
(3)有返回值,但是不写返回值类型,也不可以写void
(4)构造函数是在创建对象时自动调用
3、构造函数支持函数重载
4、构造函数也支持函数默认值
1、无参构造函数
编译器默认的构造函数:
类名(){
}
#include <iostream>
using namespace std;
class Phone{
private:
string brand; //品牌
string color; //颜色
double price; //价格
public:
void play_music() {
cout<<"播放音乐"<<endl;
}
void play_video(){
cout<<"播放视频"<<endl;
}
//无参构造函数
Phone(){
cout<<"构造函数"<<endl;
}
};
int main(){
//无参构造函数
Phone p1;
return 0;
}
2、有参构造函数
#include <iostream>
using namespace std;
class Phone{
private:
string brand; //品牌
string color; //颜色
double price; //价格
public:
void play_music() {
cout<<"播放音乐"<<endl;
}
void play_video(){
cout<<"播放视频"<<endl;
}
//有参构造函数
Phone(string a,string b,double c=2995){
brand=a;
color=b;
price=c;
}
};
int main(){
//有参构造函数(栈内存对象)
Phone p2("vivo","红",9);
//有参构造函数(堆内存对象)
Phone*p3=new Phone("oppo","蓝",4);
delete p3;
p3=NULL;
return 0;
}
3、构造初始化列表
构造初始化列表:就是构造函数的一种简便写法
注意:构造初始化列表与构造函数不能同时出现
#include <iostream>
using namespace std;
class Person{
private:
int id;
string name;
string sex;
bool flag;
public:
//构造初始化列表
Person(int id,string name,string sex,bool flag)
:id(id),name(name),sex(sex),flag(flag){}
void show(){
cout<<id<<name<<sex<<endl;
}
};
int main(){
Person p1(1001,"张三","男",true);
p1.show();
return 0;
}
4、拷贝构造函数
作用:用于实现对象的拷贝创建
特点:
(1)拷贝构造函数与构造函数构成重载(拷贝构造函数也是与类同名)
(2)如果不给出拷贝构造函数,编译器会给出默认的拷贝构造函数,完成对象之间的值复制;如果给出拷贝构造函数,编译器就不会提供默认的拷贝构造函数
(3)拷贝构造函数的参数类型是对象的引用或者是const修饰的对象的引用
(4)拷贝构造函数是在拷贝创建对象时自动调用
注意:对象之间是相互独立的,对象之间的属性也是相互独立的
4.1 浅拷贝
编译器默认给出的拷贝构造函数,完成的就是浅拷贝,会完成对象之间简单的值复制
#include <iostream>
using namespace std;
class Person{
private:
int id;
string name;
string sex;
bool flag;
public:
//构造初始化列表
Person(int id,string name,string sex,bool flag)
:id(id),name(name),sex(sex),flag(flag){}
//编译器给出的默认的拷贝构造函数(手写出来的)----->浅拷贝
Person(const Person&p){
id=p.id;
name=p.name;
sex=p.sex;
flag=p.flag;
}
void show(){
cout<<id<<name<<sex<<endl;
}
};
int main(){
Person p1(1001,"张三","男",true); //调用有参的构造函数
p1.show();
// //方法一:
// Person&pp=p1;
// Person p2(pp);
//方法二:
Person p2(p1); //调用拷贝构造函数
p2.show();
return 0;
}
默认拷贝构造函数存在安全隐患:如果成员变量是指针类型,两个对象的指针类型属性指向同一个内存空间,破坏了对象的独立性,那么这种拷贝叫浅拷贝
解决方法:使用深拷贝
4.2 深拷贝
实现方式:创建对象时,指针属性要有自己独立的区域;拷贝对象时,由地址拷贝变成内容拷贝
#include <iostream>
#include <string.h>
using namespace std;
class Animal{
private:
string kind; //种类
double weight; //体重
char*hobby; //爱好
public:
//构造函数---->深拷贝
Animal(string k,double w,char*h){
kind=k;
weight=w;
//创建对象时,指针属性要有自己独立的区域
hobby=new char[20];
strcpy(hobby,h); //使用strcpy前,需要引入string.h头文件
}
//展示信息
void show(){
cout<<"种类:"<<kind<<",体重:"<<weight<<",爱好:"<<hobby<<endl;
}
//kind读接口
string get_kind(){
return kind;
}
//拷贝构造函数---->深拷贝
Animal(const Animal&c){
kind=c.kind;
weight=c.weight;
//拷贝对象时,由地址拷贝变成内容拷贝
hobby=new char[20];
strcpy(hobby,c.hobby);
}
};
int main(){
char h[20]="eat fish";
Animal cat1("小猫",12,h); //调用有参构造函数
cat1.show();
Animal cat2(cat1); //调用拷贝构造函数
cat2.show();
return 0;
}
4.3 隐式调用构造函数
截止到目前,对于构造函数的调用都是显式调用
隐式调用构造函数的出现情况:
(1)等号赋值时,等号左侧是对象类型,等号右边恰好是对象构造函数所需要的参数类型,这时就会把右侧值传入到构造函数中,相当于隐式调用构造函数
#include <iostream>
using namespace std;
class Test{
private:
int number;
public:
Test(int number)
:number(number){}
int get_number(){
return number;
}
};
int main(){
// Test t1(100); //显示调用构造函数
// cout<<t1.get_number()<<endl;
// Test*t2=new Test(8); //显示调用构造函数
// cout<<t2->get_number()<<endl;
// delete t2;
// t2=NULL;
// Test t3(t1); //显示调用构造函数
// cout<<t3.get_number()<<endl;
Test t4=9; //隐式调用构造函数
cout<<t4.get_number()<<endl;
Test t5=t4; //隐式调用构造函数
cout<<t5.get_number()<<endl;
return 0;
}
(2)隐式调用构造函数,一般在程序员不自知的情况下产生的,需要规避掉,可以用explicit关键字屏蔽
class Test{
private:
int number;
public:
explicit Test(int number)
:number(number){}
int get_number(){
return number;
}
};