喵~
- 一、构造函数
- 1.1 默认构造函数
- 1.2 自定义的默认构造函数
- 1.3 自定义带参数的构造函数
- 二、拷贝构造函数的基本使用
- 2.1 浅拷贝和深拷贝(原理及区别)
一、构造函数
在C++面向对象的学习中,对于构造函数应该并不陌生,有默认的构造函数,也有自定义的构造函数。假设有下面类的框架
1.1 默认构造函数
class Human{
public:
void eat();
private:
//私有变量,必须提供共有get方法才能获取
string name;
int age;
};
那么此时打印的时候,因为没有在类中定义构造函数,编译器会自动调用默认构造函数,此构造函数没有参数列表没有函数体Human(){}
;是一个空的构造函数。如果没有自定义构造函数的话编译器就自动调用该构造函数,如果一旦在程序中自定义了构造函数,默认的构造函数及时作废,也就不会再被调用了。
1.2 自定义的默认构造函数
class Human{
public:
Human();
void eat();
string getName();
int getAge();
private:
//私有变量,必须提供共有get方法才能获取
string name;
int age;
};
string Human::getName(){
return name;
}
int Human::getAge(){
return age;
}
void Human::eat(){
cout << "eat" << endl;
}
Human::Human(){
name = "zhongguo";
age = 9000;
}
int main(){
Human hu;
cout << hu.getName() << endl;//zhongguo
cout << hu.getAge() <<endl;//9000
return 0;
}
自定义了构造函数,但是并没有但参数,一般将这种称之为自定义的默认构造函数,当然称为自定义的构造函数也没有什么错。上面的写法其实也可以合二为一,就是将构造函数Human()的原型和定义放在一起,放在public下面。
class Human{
public:
Human(){
name = "zhongguo";
age = 9000;
}
void eat();
string getName();
int getAge();
private:
//私有变量,必须提供共有get方法才能获取
string name;
int age;
};
1.3 自定义带参数的构造函数
这种方式是最为常见的构造函数的写法,
class Human{
public:
Human(string name,int age);
void eat();
string getName();
int getAge();
private:
//私有变量,必须提供共有get方法才能获取
string name;
int age;
};
string Human::getName(){
return name;
}
int Human::getAge(){
return age;
}
void Human::eat(){
cout << "eat" << endl;
}
Human::Human(string name,int age){
this->name = name;
this->age = age;
}
int main(){
Human hu("zhongguo",9000);
cout << hu.getName() << endl;//zhongguo
cout << hu.getAge() <<endl;//9000
return 0;
}
二、拷贝构造函数的基本使用
同样我们在程序中没有定义拷贝构造函数的时候,编译器调用的是默认的拷贝构造函数,比如上面的程序中,我们加上 Human hu("zhongguo",1000000)
;此时Human hu2 = hu
,这句就是调用了拷贝构造函数,Human hu3(hu)
这句也是。
例如下面的代码:
#include <iostream>
#include <Windows.h>
#include <string>
using namespace std;
// 定义一个“人类”
class Human {
public:
Human(int age, int salary);//自定义的有参构造函数
Human(const Human&);//自定义的拷贝构造函数
string getName();
int getAge();
int getSalary();
private:
string name = "zhonguo";
int age = 28;
int salary;
};
//自定义有参构造函数,并且参数不全
Human::Human(int age, int salary) {
cout << "调用自定义的有参构造函数" << endl;
this->age = age; //this是一个特殊的指针,指向这个对象本身
this->salary = salary;
name = "无名";
}
//拷贝构造函数都是引用
//执行Human h2 = h1; 这句时会被调用,man.name 相当于h1.name只是赋值给了h2.name
Human::Human(const Human& man) {
cout << "调用自定义的拷贝构造函数" << endl;
name = man.name;
age = man.age;
salary = man.salary;
}
string Human::getName() {
return name;
}
int Human::getAge() {
return age;
}
int Human::getSalary() {
return salary;
}
int main(void) {
Human h1(25, 35000); // 使用自定义的默认构造函数
Human h2 = h1; // 使用自定义的拷贝构造函数
//下面h2的三个成员变量的值得打印与h1完全相同
cout << "==============================" << endl;
cout << "姓名:" << h2.getName() << endl;
cout << "年龄: " << h2.getAge() << endl;
cout << "薪资:" << h2.getSalary() << endl;
system("pause");
return 0;
}
上面代码中自定义了一个拷贝构造函数,但其实在这种情况使用默认的拷贝构造函数也是一样的,但有些情况下使用默认的拷贝构造函数是有危险的。
因为自动生成的构造函数(也叫合成的拷贝构造函数)它属于“浅拷贝”,或者叫位拷贝
浅拷贝的情况下,如果一个类的成员是指针的话就会出错
见下面示例:
#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>
using namespace std;
// 定义一个“人类”
class Human {
public:
Human(int age, int salary);
//Human(const Human&); //不定义拷贝构造函数,编译器会生成“合成的拷贝构造函数”
string getName();
int getAge();
int getSalary();
void setAddr(const char* newAddr);
const char* getAddr();
private:
string name = "Unknown";
int age = 28;
int salary;
//成员变量中直接定义一个指针
char* addr;
};
Human::Human(int age, int salary) {
cout << "调用自定义的有参构造函数" << endl;
this->age = age; //this是一个特殊的指针,指向这个对象本身
this->salary = salary;
name = "无名";//将name从unknow换成无名
addr = new char[64];
//strcpy_s函数是C++中的复制字符串的函数,头文件<string.h>
strcpy_s(addr, 64, "China");
}
string Human::getName() {
return name;
}
int Human::getAge() {
return age;
}
int Human::getSalary() {
return salary;
}
void Human::setAddr(const char* newAddr) {
if (!newAddr) {
return;
}
strcpy_s(addr, 64, newAddr);
}
const char* Human::getAddr() {
return addr;
}
int main(void) {
Human h1(25, 35000); // 使用自定义的默认构造函数
Human h2 = h1; // 使用自定义的拷贝构造函数
cout << "h1 addr:" << h1.getAddr() << endl;
cout << "h2 addr:" << h2.getAddr() << endl;
h1.setAddr("长沙");
cout << "h1 addr:" << h1.getAddr() << endl;
cout << "h2 addr:" << h2.getAddr() << endl;
system("pause");
return 0;
}
结果为:
发现一个什么问题,如果使用自动生成的拷贝构造函数,Human h2 = h1
;
如果类中存在指针成员的话,那么就相当于固定在一起了,h1改变会引起h2的改变
C++ strcpy_s和strncpy_s使用方法
解决方案:在自定义的拷贝构造函数中,使用”深拷贝“
#include <iostream>
#include <Windows.h>
#include <string>
#include <string.h>
using namespace std;
// 定义一个“人类”
class Human {
public:
Human(int age, int salary);
Human(const Human &man); //如果不定义拷贝构造函数,编译器会生成“合成的拷贝构造函数”
string getName();
int getAge();
int getSalary();
void setAddr(const char* newAddr);
const char* getAddr();
private:
string name = "Unknown";
int age = 28;
int salary;
//成员变量中直接定义一个指针
char* addr;
};
Human::Human(const Human & man) {
cout << "调用了自定义的拷贝构造函数" << endl;
age = man.age;
name = man.name;
salary = man.salary;
//使用自定义的拷贝构造函数进行深拷贝
addr = new char[64];
strcpy_s(addr,64,man.addr);
}
Human::Human(int age, int salary) {
cout << "调用自定义的有参构造函数" << endl;
this->age = age; //this是一个特殊的指针,指向这个对象本身
this->salary = salary;
name = "无名";//将name从unknow换成无名
addr = new char[64];
//strcpy_s函数是C++中的复制字符串的函数,头文件<string.h>
strcpy_s(addr, 64, "China");
}
string Human::getName() {
return name;
}
int Human::getAge() {
return age;
}
int Human::getSalary() {
return salary;
}
const char* Human::getAddr() {
return addr;
}
void Human::setAddr(const char* newAddr) {
if (!newAddr) {
return;
}
strcpy_s(addr, 64, newAddr);
}
int main(void) {
Human h1(25, 35000); // 使用自定义的默认构造函数
Human h2 = h1; // 使用自定义的拷贝构造函数
cout << "h1 addr:" << h1.getAddr() << endl;
cout << "h2 addr:" << h2.getAddr() << endl;
h1.setAddr("长沙");
cout << "h1 addr:" << h1.getAddr() << endl;
cout << "h2 addr:" << h2.getAddr() << endl;
system("pause");
return 0;
}
由上面结果能看出,h2的地址并没有发生改变
2.1 浅拷贝和深拷贝(原理及区别)
关于深拷贝和浅拷贝的区别可以根据拷贝构造函数来分析
一般默认的拷贝构造函数使用的就是浅拷贝,它是将原来对象的引用直接复制给新对象了,因为标准的拷贝构造是引用类型嘛,这个时候其实新旧对象因为引用相同所以指向的值是相同的,改变原对象的值新对象的值也会随之改变
而深拷贝是创建了一个新的对象,然后将原对象中各种属性的值拷贝过去了,这个时候如果原对象被重新赋值的话,新对象的值还是保持原来的值不变。
根据我们上面的例子,如果成员对象中存在指针的话,必须要使用深拷贝
浅拷贝
深拷贝