目录
1. 构造函数
1.1 基本使用
构造函数是一种特殊的成员函数,用于创建对象时初始化,写法上有以下要求:
● 函数名称必须与类名完全相同。
● 构造函数不写返回值
● 如果程序员不手动编写构造函数,编译器会自动添加一个默认无参的构造函数。
● 手动添加构造函数后,编译器就不会自动添加默认无参构造函数了。
● 构造函数在创建对象时,常用于给对象的属性赋予初始值。
● 构造函数也支持构造函数重载
● 也支持函数参数默认值
利用构造函数赋初值:
无参构造函数创建对象时:在构造函数内进行默认值进行初始化,
有参构造函数创建对象时,通过传入的参数进行初始化。
#include <iostream>
using namespace std;
// 构造函数---
class MoblePhone1
{
private:
string bread;
string model;
int weight = 200;
public: // 权限:最开放的权限
MoblePhone1() // 无参
{
bread = "8848";
model = "鳄鱼皮";
weight = 5500;
cout << "无参构造函数被调用了" << endl;
}
MoblePhone1(string b,string m,int w) // 有参
{
bread = b;
model = m;
weight = w;
cout << "有参构造函数被调用了" << endl;
}
string get_bread()
{
return bread;
}
void set_bread(string b)
{
bread = b;
}
string get_model()
{
return model;
}
void set_model(string c)
{
model = c;
}
int get_weight()
{
return weight;
}
};
int main()
{
// 构造函数
// 栈内存
cout << "栈内存,无参" << endl;
MoblePhone1 mp3; // 无参
cout << mp3.get_bread() << endl;
cout << mp3.get_model() << endl;
cout << mp3.get_weight() << endl;
cout << endl;
cout << "栈内存,有参" << endl;
MoblePhone1 mp4("小米","14Pro", 500); // 有参
cout << mp4.get_bread() << endl;
cout << mp4.get_model() << endl;
cout << mp4.get_weight() << endl;
cout << endl;
// 堆内存
cout << "堆内存,无参" << endl;
MoblePhone1 *mp5 = new MoblePhone1; // 无参
cout << mp5->get_bread() << endl;
cout << mp5->get_model() << endl;
cout << mp5->get_weight() << endl;
cout << endl;
cout << "堆内存,有参" << endl;
MoblePhone1 *mp6 = new MoblePhone1("红米","70ulurt",500); // 有参
cout << mp6->get_bread() << endl;
cout << mp6->get_model() << endl;
cout << mp6->get_weight() << endl;
cout << endl;
return 0;
}
1.2 函数参数默认值
函数参数默认值,从一个添加默认值的变量开始,后边的变量都要添加默认值。
比如 MoblePhone2(string b ,string m = "13Pro",int w = 200) // 有参,string m 赋予了默认值,那么后面的int w也必须赋予默认值。
无参函数与全部参数都加默认值的函数,调用时候写法一样,不能同时存在。因为编译器无法区分。
#include <iostream>
using namespace std;
// 构造函数---函数默认值
class MoblePhone2
{
private:
string bread;
string model;
int weight = 200;
public: // 权限:最开放的权限
// 无参函数与全部参数都加默认值的函数,调用时候写法一样;
// 不能同时存在。因为编译器无法区分
MoblePhone2() // 无参
{
bread = "8848";
model = "鳄鱼皮";
weight = 5500;
cout << "无参构造函数被调用了" << endl;
}
// MoblePhone2(string b = "8848",string m = "鳄鱼皮",int w = 5500) // 有参
// {
// bread = b;
// model = m;
// weight = w;
// }
MoblePhone2(string b ,string m = "13Pro",int w = 200) // 有参
{
bread = b;
model = m;
weight = w;
cout << "默认参数构造函数被调用了" << endl;
}
string get_bread()
{
return bread;
}
void set_bread(string b)
{
bread = b;
}
string get_model()
{
return model;
}
void set_model(string c)
{
model = c;
}
int get_weight()
{
return weight;
}
};
int main()
{
// 函数默认值
MoblePhone2 mp7;
cout << mp7.get_bread() << endl;
cout << mp7.get_model() << endl;
cout << mp7.get_weight() << endl;
cout << endl;
MoblePhone2 mp8("小米");
cout << mp8.get_bread() << endl;
cout << mp8.get_model() << endl;
cout << mp8.get_weight() << endl;
cout << endl;
return 0;
}
1.3 构造初始化列表
构造初始化列表是一种更简单的给成员变量赋予初始值的写法。
当构造函数的局部变量与成员变量重名时,除了this指针的方式外,也可以使用构造初始化列表进行区分。
如果成员函数的形参名字与成员变量的名字一样,那么在赋值时就会出现类似 a = a 的情况,这时候编译器会分不清形参与成员变量,这时候可以用初始化列表进行区分。
利用构造初始化列表进行初始化赋值:
#include <iostream>
using namespace std;
// 构造初始化列表
class MoblePhone3
{
private:
string bread;
string model;
int weight = 200;
public: // 权限:最开放的权限
MoblePhone3(string b,string m,int w) // 有参
:bread(b),model(m),weight(w)
{
cout << "构造初始化列表" << endl;
}
string get_bread()
{
return bread;
}
void set_bread(string b)
{
bread = b;
}
string get_model()
{
return model;
}
void set_model(string c)
{
model = c;
}
int get_weight()
{
return weight;
}
};
int main()
{
// 构造初始化列表
MoblePhone3 mp9("小米","14Pro",500);
cout << mp9.get_bread() << endl;
cout << mp9.get_model() << endl;
cout << mp9.get_weight() << endl;
cout << endl;
return 0;
}
1.4 隐式调用构造函数
构造函数的调用可以分为显式调用和隐式调用。
显式调用指的是在创建对象的时候手写构造函数的名称和参数列表。
隐式调用指的是在创建对象时不写构造函数的参数列表,编译器会尝试调用对应参数的构造函数。
隐式调用实例:
Student s1(13); // 显式调用
Student s2 = Student(14); // 显式调用
像以上显式调用可以直接根据传入的参数来判断调用构造函数,并且直接进行性对应成员变量的初始化赋值。
Student s3 = 15; // 隐式调用
以上隐式调用,编译器需要先自行判断进行初始化赋值的成员变量。
#include <iostream>
using namespace std;
// 隐是调用
class Student
{
private:
int age;
public:
// 隐式调用
Student(int a)
:age(a)
{
cout << "构造函数" << endl;
}
explicit Student(int a,int b) // 屏蔽隐式调用
:age(a)
{
cout << "构造函数" << endl;
}
int get_age()
{
return age;
}
};
int main()
{
//隐式调用构造函数
Student s1(13); // 显式调用
cout << s1.get_age() <<endl;
cout << endl;
Student s2 = Student(14); // 显式调用
cout << s2.get_age() << endl;
cout << endl;
Student s3 = 15; // 隐式调用
cout << s3.get_age() << endl;
cout << endl;
Student *s4 = new Student(16);
cout << s4->get_age() << endl;
cout << endl;
return 0;
}
建议使用显式调用,可以使用 explicit 关键字屏蔽隐式调用语法。
2. 拷贝构造函数
2.1 概念
当用一个对象给另一个对象进行二次赋值时,就需要用到拷贝构造函数。
当程序员不手写拷贝构造函数时,编译器会自动添加一个拷贝构造函数,使对象的创建可以通过这个构造函数实现。
利用拷贝及构造函数进行赋值实例:
用对象s1 给对象 s2 进行赋值,会调用自己定义的构造函数 Student1(const Student1 &st) 。
#include <iostream>
using namespace std;
// 拷贝构造函数
class Student1
{
private:
int age;
public:
Student1(int a)
:age(a)
{
cout << "构造函数" << endl;
}
// 拷贝构造函数
Student1(const Student1 &st)
{
age = st.age;
cout << "调用了拷贝构造函数" << endl;
}
int get_age()
{
return age;
}
};
int main()
{
//拷贝构造函数
Student1 st1(12);
Student1 st2(st1);
cout << "st1.get_age:" << " " << st1.get_age() << endl;
cout << "st2.get_age:" << " " << st2.get_age() << endl;
return 0;
}
2.2 浅拷贝
【思考】:拷贝构造函数会存在隐患吗?
存在,当成员变量存在指针类型时,默认的拷贝构造函数会导致两个对象的成员变量指向同一处,不符合面向对象的设计规范,这种现象被称为“浅拷贝”。
// 浅拷贝更改外部内存,当对任意一个对象进行操作时,因为几个拷贝的对象是指向同一个空间,所以几个对象内部的数据也被更改了, 因为操作的都是同一块内存 。
浅拷贝实例:
刚开始对象d1初始化赋值为 “旺财”,将通过拷贝及构造函数,用d1 给d2进行赋值,然后对d2进行修改,会发现对象d1的成员变量的值跟对象d2成员变量的值都发生了改变。
#include <iostream>
#include <string.h>
using namespace std;
// 浅拷贝
class Dog
{
private:
char *name;
public:
Dog(char *n)
{
name = n;
}
void show_dog()
{
cout << name << endl;
}
};
int main()
{
// 浅拷贝
char dog[32] = "旺财";
Dog d1(dog); // 赋值
Dog d2(d1); // 调用拷贝构造函数
cout << "修改前" << endl;
d1.show_dog(); // 旺财
d2.show_dog(); // 旺财
cout << endl;
cout << "修改后" << endl;
strcpy(dog,"大黄");
d1.show_dog(); // 大黄
d2.show_dog(); // 大黄
cout << endl;
return 0;
}
2.3 深拷贝
如果要解决浅拷贝存在的问题,就必须在拷贝时开辟一块新的空间,这种情况必须手动重写构造函数,使每次赋值都创建一个新的副本,从而每个对象单独持有自己的成员变量。这种方式被称为“深拷贝”。
【思考】深拷贝的代码是否存在隐患?
存在,new开辟的空间没有释放,造成内存泄漏的问题。
深拷贝实例:
用dog数组给对象d3 和 d4进行赋值,因为采用的是深拷贝,当arr改变之后,对d3 和d4的成员变量没有影响,依旧是旺财。
#include <iostream>
#include <string.h>
using namespace std;
// 深拷贝
class Dog2
{
private:
char *name;
public:
Dog2(char *n)
{
name = new char[32];
strcpy(name,n);
}
Dog2(const Dog2 &d)
{
name = new char[32];
strcpy(name,d.name);
}
void show_dog()
{
cout << name << endl;
}
};
int main()
{
// 深拷贝
char dog2[32] = "旺财";
Dog2 d3(dog2); // 赋值
Dog2 d4(d3); // 调用拷贝构造函数
cout << "修改前" << endl;
d3.show_dog(); // 旺财
d4.show_dog(); // 旺财
cout << endl;
cout << "修改后" << endl;
strcpy(dog2,"大黄");
d3.show_dog(); // 旺财
d4.show_dog(); // 旺财
cout << endl;
return 0;
}
3. 析构函数
析构函数是与构造函数对立的函数。
delete 销毁数组时,需要添加 []
构造函数 | 析构函数 |
创建对象时手动调用 | 当对象销毁时,自动调用 |
函数名称是 类名 | 函数名称是 ~类名 |
构造函数可以重载 | 析构函数没有参数,不能重载 |
用于创建对象时并初始化 | 用于销毁对象时释放资源 |
有返回值但是不写,返回值是新创建的对象 | 没有返回值 |
// 析构函数
~Dog()
{
cout <<"我被调用了"<< endl;
delete []name;// []表示销毁数组
}
标签:初始化,调用,string,C++,面向对象,拷贝,默认值,构造函数
From: https://blog.csdn.net/weixin_67273669/article/details/140891144