构造函数是一种特殊的成员函数,用于创建对象,写法上有以下要求:
手动添加构造函数,编译器就不会自动添加构造函数。
#include
using namespace std;
// 帕斯卡命名法(大驼峰命名法)
// 每个单词首字母大写
class MobilePhone
{
private: // 私有权限,最封闭的权限,只能在类内访问
string brand; // 品牌
string modle; // 型号
int weight; // 重量
public: // 权限:public是最开放的权限。
MobilePhone() // 默认构造函数
{
brand = "8848";
modle = "M6 巅峰版";
weight = 500;
}
// 品牌的get函数
string get_brand()
{
return brand;
}
// 型号的get函数
string get_modle()
{
return modle;
}
// 重量的get函数
int get_weight()
{
return weight;
}
// 品牌的set函数
void set_brand(string b)
{
brand = b;
}
};
int main()
{
// 栈内存对象
MobilePhone mp1;
cout << mp1.get_brand() << endl;
cout << mp1.get_modle() << endl;
cout << mp1.get_weight() << endl;
return 0;
}
#include
using namespace std;
// 帕斯卡命名法(大驼峰命名法)
// 每个单词首字母大写
class MobilePhone
{
private: // 私有权限,最封闭的权限,只能在类内访问
string brand; // 品牌
string modle; // 型号
int weight; // 重量
public: // 权限:public是最开放的权限。
MobilePhone() // 默认构造函数
{
brand = "8848";
modle = "M6 巅峰版";
weight = 500;
}
// 有参构造函数(函数重载)
MobilePhone(string b,string m,int w)
{
brand = b;
modle = m;
weight = w;
}
// 品牌的get函数
string get_brand()
{
return brand;
}
// 型号的get函数
string get_modle()
{
return modle;
}
// 重量的get函数
int get_weight()
{
return weight;
}
// 品牌的set函数
void set_brand(string b)
{
brand = b;
}
};
int main()
{
// 栈内存对象
MobilePhone mp1("vivo","iqoo12",300);
cout << mp1.get_brand() << endl;
cout << mp1.get_modle() << endl;
cout << mp1.get_weight() << endl;
return 0;
}
在创建对象的时候,必须调用任意构造函数。
#include
using namespace std;
// 帕斯卡命名法(大驼峰命名法)
// 每个单词首字母大写
class MobilePhone
{
private: // 私有权限,最封闭的权限,只能在类内访问
string brand; // 品牌
string modle; // 型号
int weight; // 重量
public: // 权限:public是最开放的权限。
MobilePhone() // 默认构造函数
{
brand = "8848";
modle = "M6 巅峰版";
weight = 500;
}
// 有参构造函数
MobilePhone(string b,string m,int w)
{
brand = b;
modle = m;
weight = w;
}
MobilePhone(int w,string b = "oppo",string m = "findx")
{
brand = b;
modle = m;
weight = w;
}
// 品牌的get函数
string get_brand()
{
return brand;
}
// 型号的get函数
string get_modle()
{
return modle;
}
// 重量的get函数
int get_weight()
{
return weight;
}
// 品牌的set函数
void set_brand(string b)
{
brand = b;
}
};
int main()
{
// 栈内存对象
MobilePhone mp1;
cout << mp1.get_brand() << endl;
cout << mp1.get_modle() << endl;
cout << mp1.get_weight() << endl;
MobilePhone *mp2 = new MobilePhone();
return 0;
}
构造初始化列表是一种更简单的给成员变量赋予初始值的写法。传参遵寻就近原则。
注意:当构造函数的局部变量与成员变量同名时,除了使用后面学习的this指针外,也可以使用构造初始化列表的方式进行区分。
#include
using namespace std;
// 帕斯卡命名法(大驼峰命名法)
// 每个单词首字母大写
class MobilePhone
{
private: // 私有权限,最封闭的权限,只能在类内访问
string brand; // 品牌
string modle; // 型号
int weight; // 重量
public: // 权限:public是最开放的权限。
// 构造初始化列表:编译器成员变量与局部变量可以做区分
MobilePhone()://直接赋值,成员变量已经创建。
brand("8848"),modle("M6 巅峰版"),weight(500){}
MobilePhone(string brand,string modle,int weight)://默认传参
brand(brand),modle(modle),weight(weight){}
//对象创造之前被调用,还没有创造对象,成员变量已经创建。不能放到函数体内,函数无法识别局部变量与成员变量
// 品牌的get函数
string get_brand()
{
return brand;
}
// 型号的get函数
string get_modle()
{
return modle;
}
// 重量的get函数
int get_weight()
{
return weight;
}
};
int main()
{
// 栈内存对象
MobilePhone mp1;
cout << mp1.get_brand() << endl;
cout << mp1.get_modle() << endl;
cout << mp1.get_weight() << endl;
MobilePhone *mp2 = new MobilePhone("三星","note7",100);
cout << mp2->get_brand() << endl;
cout << mp2->get_modle() << endl;
cout << mp2->get_weight() << endl;
delete mp2;
mp2 = NULL;
return 0;
}
截至到目前为止,对于构造函数的调用都是显式调用的。
#include
using namespace std;
class Test
{
private:
int val;
public:
Test(int v,int i,string str,double d)
{
val = v;
cout << "构造函数" << endl;
}
int get_val()
{
cout << &val << endl;
return val;
}
};
int main()
{
// Test t1(2); // 显式调用构造函数
// cout << t1.get_val() << endl;
Test t2 = {123,2,"string",2.4}; // 隐式调用构造函数,多参数传递C++11支持
cout << t2.get_val() << endl;
return 0;
}
当程序员不手写拷贝构造函数时,编译器会自动添加一个拷贝构造函数,使对象创建可以通过这个构造函数进行实现。
#include
using namespace std;
class Test
{
private:
int val;
public:
Test(int v)
{
val = v;
cout << "构造函数" << endl;
}
// 手动添加默认的拷贝构造函数
Test(Test &t)
{
val = t.val;
}
int get_val()
{
return val;
}
};
int main()
{
Test t1(4);
cout << t1.get_val() << endl;
Test t2(t1); // 调用拷贝构造函数
cout << t2.get_val() << endl;
return 0;
}
没有任何构造函数的时候:编译器会给我们创建一个默认构造函数、拷贝构造函数。
如果我们有一个默认构造函数,编译器不会创建默认构造函数,会创建拷贝构造函数。
如果我们有一个有参构造函数,编译器不会创建默认构造函数,会创建拷贝构造函数。
如果我们有一个拷贝构造函数,编译器不会创建默认构造函数,不会创建拷贝构造函数。
【思考】:拷贝构造函数会存在隐患吗?
存在,当成员变量出现指针类型时,默认的拷贝构造函数会导致两个对象的成员变量指向同一处,改变一处另一处也会改变不符合面向对象的设计规范,这种现象被称为”浅拷贝“。
存在的问题:更改一处三处都会改变
#include
#include
using namespace std;
class Dog
{
private:
char *name;
public:
Dog(char *n)
{
name = n;
}
void show_name()
{
cout << name << endl;
}
};
int main()
{
char arr[20] = "旺财";
Dog d1(arr);
Dog d2(d1); // 调用的是拷贝构造函数,不调用构造函数
strcpy(arr,"大黄");//两个都会改变
d1.show_name(); // 大黄
d2.show_name(); // 大黄
return 0;
}
这种情况必须要手写构造函数,使每次赋值都创建一个新的副本,从而每个对象单独持有自己的成员变量,这种方式被称为“深拷贝”。
new开辟的空间无法释放,造成内存泄漏的问题。
#include
#include
using namespace std;
class Dog
{
private:
char *name;
public:
Dog(char *n)
{
name = new char[20];
strcpy(name,n);
}
Dog(Dog &d)
{
name = new char[20];
strcpy(name,d.name);
}
void show_name()
{
cout << name << endl;
}
};
int main()
{
char arr[20] = "旺财";
Dog d1(arr);
Dog d2(d1); // 拷贝构造函数
strcpy(arr,"大黄");
d1.show_name(); // 旺财
d2.show_name(); // 旺财
return 0;
}
#include
#include
using namespace std;
class Dog
{
private:
char *name;
public:
// 构造函数,初始化对象的name成员变量
Dog(char *n)
{
name = n; // 将传入的指针赋值给name,这会导致问题后面会解释
}
// 拷贝构造函数,用于创建一个新的对象并复制已有对象的数据
Dog(Dog &d)
{
name = new char[20]; // 为name分配内存空间
strcpy(name, d.name); // 复制参数对象的name值到当前对象的name
}
// 打印狗的名字
void show_name()
{
cout << name << endl;
}
};
int main()
{
char arr[20] = "旺财"; // 创建一个字符数组,并存储字符串"旺财"
Dog d1(arr); // 创建一个Dog对象d1,传入arr作为参数
Dog d2(d1); // 使用拷贝构造函数,创建一个新的Dog对象d2,复制d1的数据
strcpy(arr, "大黄"); // 修改arr中的字符串为"大黄"
d1.show_name(); // 输出:旺财,因为d1的构造函数只是将name指针指向arr的地址,所以修改arr后,d1的name也改变了
d2.show_name(); // 输出:旺财,因为拷贝构造函数在创建d2时复制了d1的name,所以不受arr的修改影响
return 0;
}
【思考】 深拷贝的代码是否存在隐患?
存在,new开辟的空间无法释放,造成内存泄漏的问题。