定义一个类
const double PI = 3.14;
class Circle{ private: int m_r;//半径
//获取周长
public: double calculateZC(){
return 2 * PI * m_r;
}
//设置半径
public: void setR(int r){
m_r = r;
}
};
int main() { Circle circle; circle.setR(20);
cout << circle.calculateZC() << endl;
return 0;
}
> 125.6
<a name="QOHK2"></a>
### 构造函数及析构函数
默认情况下,编译器至少会给一个类自动生成三个函数
- 默认构造函数,函数体为空
- 默认析构函数,函数体为空
- 默认拷贝构造函数,对属性的值进行拷贝
这三个方法都可以不声明,编译器会自动添加实现。构造主要用于做一些初始化操作,析构则是进行清理工作,拷贝函数则用于拷贝当前对象(如值传递)。
```cpp
#include <iostream>
using namespace std;
const double PI = 3.14;
class Circle{
//无参/默认 构造函数
public:Circle(){}
//有参析构函数
public:Circle(int r){
m_r = r;
}
private: int m_r;//半径
//获取周长
public: double calculateZC(){
return 2 * PI * m_r;
}
//设置半径
public: void setR(int r){
if(r>0){
m_r = r;
}
}
//设置半径
public: int getR(){
return m_r;
}
//析构函数,回收对象时会执行该方法
~Circle(){
}
};
int main()
{
Circle circle1;//调用默认构造函数不可以加括号,因为编译器会以为在调用函数
circle1.setR(1);
Circle circle2(2);//调用有参数构造函数的方法一
Circle circle3 = Circle(3);//调用有参构造函数的方法二
Circle circle4 = 4;//调用有参构造函数的方法三
cout <<"第一个圆的半径为:"<<circle1.getR()<<",周长为:"<< circle1.calculateZC() << endl;
cout <<"第二个圆的半径为:"<<circle2.getR()<<",周长为:"<< circle2.calculateZC() << endl;
cout <<"第三个圆的半径为:"<<circle3.getR()<<",周长为:"<< circle3.calculateZC() << endl;
cout <<"第四个圆的半径为:"<<circle4.getR()<<",周长为:"<< circle4.calculateZC() << endl;
return 0;
}
第一个圆的半径为:1,周长为:6.28 第二个圆的半径为:2,周长为:12.56 第三个圆的半径为:3,周长为:18.84 第四个圆的半径为:4,周长为:25.12
拷贝构造函数
如果没有声明拷贝构造函数,则编译器会自动生成,所以如果不需要对拷贝的内容进行调整,不需要声明该方法
#include <iostream>
using namespace std;
const double PI = 3.14;
class Circle{
//无参/默认 构造函数
public:Circle(){
cout << "调用默认构造函数" << endl;
}
//拷贝构造函数(参数必须是常量引用且同类型)
public:Circle(const Circle& c){
cout << "调用拷贝构造函数" << endl;
m_r = c.m_r;
}
private: int m_r;//半径
//获取周长
public: double calculateZC(){
return 2 * PI * m_r;
}
//设置半径
public: void setR(int r){
if(r>0){
m_r = r;
}
}
//设置半径
public: int getR(){
return m_r;
}
//析构函数,回收对象时会执行该方法
~Circle(){
}
};
void doWork(Circle c){
cout <<"圆的半径为:"<<c.getR()<<",周长为:"<< c.calculateZC() << endl;
}
int main()
{
Circle circle1;//调用默认构造函数不可以加括号,因为编译器会以为在调用函数
circle1.setR(1);
Circle circle2(circle1);//调用拷贝构造函数初始化对象
doWork(circle1);//由于调用函数是值传递,值传递时编译器会自动调用拷贝构造函数然后创建一个临时对象进行传参
return 0;
}
编译器调用拷贝构造函数的时机
- 开发者主动调用拷贝函数进行初始化的情况
- 采用值传递的方式进行函数传参时,编译器会调用拷贝构造函数对传参的对象进行拷贝
默认拷贝函数的问题
编译器的默认实现是浅拷贝,这种情况如果数据是存在堆上(也就是new出来的),那么浅拷贝会出现问题。
- 浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间(new),进行拷贝操作
一个浅拷贝带来的问题的案例
错误版本:
#include <iostream>
using namespace std;
const double PI = 3.14;
class Circle{
public:int* m_r = 0;//半径
public:Circle(int r){
m_r = new int(r);
}
//拷贝构造函数(参数必须是常量引用且同类型)
public:Circle(const Circle& c){
cout << "调用拷贝构造函数" << endl;
m_r = c.m_r;//这里是浅拷贝,也是编译器的默认实现
}
~Circle(){
if(m_r != NULL){
delete m_r;//最终问题出在这里,m_r由于是浅拷贝,两个对象里delete的其实是同一个变量,delete了两次
}
}
};
int main()
{
Circle circle1(12);
Circle circle2(circle1);//调用拷贝构造函数初始化对象
return 0;
}
修改后:
#include <iostream>
using namespace std;
const double PI = 3.14;
class Circle{
public:int* m_r = 0;//半径
public:Circle(int r){
m_r = new int(r);
}
//拷贝构造函数(参数必须是常量引用且同类型)
public:Circle(const Circle& c){
cout << "调用拷贝构造函数" << endl;
m_r = new int(*c.m_r);//开辟新的堆空间
}
~Circle(){
if(m_r != NULL){
delete m_r;
}
}
};
int main()
{
Circle circle1(12);
Circle circle2(circle1);//调用拷贝构造函数初始化对象
return 0;
}