一道题目让你弄清c++类中的(无参、有参)构造函数、拷贝构造函数、析构函数、输出流重载、四则运算符重载
-
下面简单介绍下理论知识,后面用一道题实操解释,代码带有注释讲解。
-
类的构造函数的名称与类的名称同名,不返回任何类型,它在每次创建类的新对象时自动调用。
1.无参构造函数
无参构造函数即默认调用的构造函数。
2.有参构造函数
创建新对象时,传进去对应参数,即可初始化。
3.拷贝构造函数
它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。
在以下三种情况下系统会自动调用拷贝构造函数:
1.用同类型的对象初始化新创建的对象
2.当函数的形参是类的对象时(也就是值传递时),注意:如果是引用传递则不会调用。
3.当函数的返回值是类的对象或引用时。
长这个样子,也是和类同名,注意形参:
classname (const classname &obj)
{
// 构造函数的主体
}
4.析构函数
析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值
也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。在每次删除所创建的对象时执行。或程序跑完时执行即释放资源。
5.输出流重载
C++ 能够使用流提取运算符 >> 和流插入运算符 << 来输入和输出内置的数据类型。您可以重载流提取运算符和流插入运算符来操作对象等用户自定义的数据类型。
在这里,有一点很重要,我们需要把运算符重载函数声明为类的友元函数,这样我们就能不用创建对象而直接调用函数。
- 友元函数
- 有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
- 友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
- 如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend,
您可以重定义或重载大部分 C++ 内置的运算符。这样,您就能使用自定义类型的运算符。
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
operator<<()、operator+()…
下面看一道题目:
代码(代码有注释讲解别慌):
#include<iostream>
#include<stdlib.h>
#include<algorithm>
#include<cmath>
using namespace std;
//求最大公约数
int gcd(int a, int b)
{
//辗转相除法
return a % b ? gcd(b, a%b) : b;
}
//求最大公倍数
int lcm(int a, int b)
{
return a * b / gcd(a, b);
}
class Fraction
{
private: int molecule, denominator;
public:
//默认构造函数
Fraction()
{
molecule = 1;
denominator = 1;
//cout << "default construction: ";
}
//有参构造函数,参数为:分子、分母
Fraction(int m, int d)
{
if (m != 0 && d != 0)
{
if (m < 0 && d < 0) {
m = abs(m); d = abs(d); }
//为了方便后面计算时,通分方便
else if(m>0&&d<0) {
m = -abs(m); d = abs(d); }
molecule = m / gcd(abs(m), abs(d));
denominator = d / gcd(abs(m), abs(d));
}
else
{
molecule = m;
denominator = d;
}
}
//拷贝构造函数
Fraction(const Fraction &obj)
{
//obj为原始对象,现在需要返回一个拷贝的对象,将obj的成员拷贝给这个对象
molecule = obj.molecule;
denominator = obj.denominator;
cout << "调用拷贝构造函数"<<endl;
}
//析构函数
~Fraction()
{
cout << "调用析构函数"<<endl;
}
//输出流重载
//实际上流操作符左侧必须为cin或cout,即istream或ostream类,不是我们能修改的类。
//这导致我们不能用成员函数重载,只能使用类外普通函数重载。
//由于这里我们将类内部的私有变量进行输入输出,所以重载函数必须有对内部成员访问权限。
//molecule, denominator是类的私有变量,所有通过友元函数可以访问。
friend ostream &operator<<(ostream &output, const Fraction &F)
{
//algorithm头文件里是有这个函数的但不知道为啥VS里导入没有,dev可以
//__gcd(a, b);求两数的最大公约数(前面有两个下划线),这里只能自己写个了
//这两中情况算不了最大公约数,所以先判断
if (F.denominator == 0)
{
output << "NaN";
return output;
}
else if (F.molecule == 0)
{
output << F.molecule;
return output;
}
int m, d;
m = F.molecule;
d = F.denominator;
if (d == 1) cout << m;
else if(m<0&&d<0) output << abs(m) << '/' << abs(d);
else if(m<0&&d>0) output <<'-'<<abs(m) << '/' << d;
else if (m > 0 && d < 0)
{
if (d == -1) output << '-' << m;
else output << '-' << m << '/' << abs(d);
}
else output <<m<<'/'<<d;
return output;
}
/*四则运算符重载*/
//+运算符重载
Fraction operator+(const Fraction &F)
{
Fraction temp;
int lcm_num = lcm(this->denominator, F.denominator);
temp.molecule = this->molecule*(lcm_num / this->denominator) + F.molecule*(lcm_num / F.denominator);
temp.denominator = lcm_num;
return temp; //对象作为返回值,这里会调用一次拷贝构造函数
}
//-运算符重载
Fraction operator-(const Fraction &F)
{
Fraction temp;
int lcm_num = lcm(this->denominator, F.denominator);
temp.molecule = this->molecule*(lcm_num / this->denominator) - F.molecule*(lcm_num / F.denominator);
temp.denominator = lcm_num;
return temp; //对象作为返回值,这里会调用一次拷贝构造函数
}
//*运算符重载
Fraction operator*(const Fraction &F)
{
Fraction temp;
temp.molecule = this->molecule*F.molecule;
temp.denominator = this->denominator*F.denominator;
return temp;
}
//+运算符重载
Fraction operator/(const Fraction &F)
{
Fraction temp;
if (F.molecule == 0 || F.denominator == 0)
{
//因为这种情况会造成分式分母为0的情况,所以直接赋值为0;
//输出这个分数时会判断为NaN
temp.molecule = 0;
temp.denominator = 0;
}
else
{
temp.molecule = this->molecule * F.denominator;
temp.denominator = this->denominator * F.molecule;
}
return temp;
}
};
int main()
{
Fraction f; ///则调用默认构造函数
Fraction f1(1,2);
Fraction f2(2,5);
Fraction f3;
f3 = f1 / f2;
cout << "************"<<f1<<' '<<f2<<' '<<f3;
cout << endl;
cout<<"结束啦帅哥!!!"<<endl;
system("pause");
return 0;
}