C++:结构体和类
在之前的博客中已经讲过了C语言中的结构体概念了,重复的内容在这儿就不赘述了。C++中的结构体在C语言的基础上还有些补充,在这里说明一下,顺便简单地讲一下类的概念。
一、成员函数
struct tag
{
成员变量列表; //成员变量可以有1个,也可以有多个
成员函数列表; //成员函数可以有,也可以没有
} 结构体变量列表; //在声明结构体类型的同时,创建结构体变量,可以有多个,中间使⽤逗号隔开
我们可以发现C++中的结构体和C语言结构体的有一个比较大的差异就是:C++中的结构体中除了有成员变量之外,还可以包含成员函数。下面先概要地介绍一下成员函数:
(1)C++的结构体会有一些默认的成员函数,比如:构造函数、析构函数等,是编译器默认生成的,如果觉得不合适,也是可以自己显示的定义这些函数,这些函数都是自动被调用,不需要手动调用。
(2)除了默认的成员函数之外,我们可以自己定义一些成员函数,这些成员函数可以有,也可以没有,完全根据的实际的需要来添加就可以。
(3)这些成员函数可以直接访问成员变量。
#include <iostream>
using namespace std;
struct Stu
{
//成员变量
string name;
int chinese;
int math;
int total;
//构造函数
Stu()
{
cout << "调用构造函数" << endl;
name = "小明";
chinese = 99;
math = 95;
total = math + chinese;
}
//析构函数
~Stu()
{
cout << "调用析构函数" << endl;
//对于当前这个类的对象没有啥资源清理
}
};
int main()
{
struct Stu s;
return 0;
}
运行结果打印了调用构造函数和调用析构函数这两句话,这也验证了构造函数和析构函数是会被自动调用的。
#include <iostream>
using namespace std;
struct Stu
{
//成员变量
string name;
int chinese;
int math;
int total;
//构造函数
Stu()
{
name = "小明";
chinese = 99;
math = 95;
total = math + chinese;
}
void print_stu() //打印
{
cout << "名字:" << name << endl;
cout << "语文:" << chinese << endl;
cout << "数学:" << math << endl;
cout << "总分:" << total << endl;
}
};
int main()
{
struct Stu s;
s.print_stu();
return 0;
}
二、运算符重载
int n = 100;
float f = 3.14f;
cout << n << endl;
cout << f << endl;
#include <iostream>
using namespace std;
struct Stu
{
//成员变量
string name;
int chinese;
int math;
int total;
};
//重载输出运算符
ostream& operator<<(ostream& os, const Stu & s)
{
os << "名字: " << s.name << endl;
os << "语文: " << s.chinese << endl;
os << "数学: " << s.math << endl;
os << "总分: " << s.total << endl;
return os;
}
int main()
{
struct Stu s = {"张三", 90, 80, 170};
cout << s << endl;
return 0;
}
ostream&:表示该函数返回一个指向 ostream 对象的引用。ostream 是C++中用于输出流的标准库类,例如 cout 就是 ostream 类型的一个实例。
operator<<:这是重载运算符的语法,允许你定义当使用 `<<` 运算符时应该执行的操作。这里我们重载了 << 运算符,使其能够接受 ostream 和 Stu 类型的参数,并按特定格式输出对象的信息。
(ostream& os, const Stu & s):这个函数接受两个参数,第一个是 ostream 类型的引用 os,代表输出流(如 cout),第二个是一个常量引用 const Stu & s,即要输出的 Stu 类型对象。
接下来就简单地讲集几种运算符重载。
1.加号运算符重载
#include <iostream>
using namespace std;
struct Stu
{
int a;
float b;
Stu operator+(Stu s)
{
Stu s3;
s3.a=a+s.a;
return s3;
}
}s1,s2;
int main()
{
s1.a=1;
s2.a=2;
Stu s3=s1+s2;
cout << s3.a;
return 0;
}
在这段代码中,Stu operator+(Stu s) 是一个成员函数,用于重载加法运算符(+),以便两个 Stu 类型的对象可以相加。它接受一个 Stu 类型的参数 s 并返回一个 Stu 类型的结果。由于这是一个成员函数,它隐式地接收调用它的对象作为第一个参数。这个运算符重载为s1.operator+(s2),可以简化为s1+s2。当然我们也可以使用全局函数的方式来实现运算符的重载。
#include <iostream>
using namespace std;
struct Stu
{
int a;
float b;
}s1,s2;
Stu operator+(Stu s1,Stu s2)
{
Stu s3;
s3.a=s1.a+s2.a;
return s3;
}
int main()
{
s1.a=1;
s2.a=2;
Stu s3=s1+s2;
cout << s3.a;
return 0;
}
这个运算符重载为operator+(s1,s2),可以简化为s1+s2。
2.左移运算符重载
最上面举的cout的例子就是左移运算符重载,是采取全局函数的方式来实现的。在这里顺便一提,在这里如果我们使用成员函数的方式是实现不了的,在下面代码中我们可以看出,虽然能够重载这个运算符,但是实现的效果为s << cout ,并不是我们想要的结果。
#include <iostream>
using namespace std;
struct Stu
{
//成员变量
string name;
int chinese;
int math;
int total;
//重载输出运算符
ostream& operator<<(ostream& os)
{
os << "名字: " << name << endl;
os << "语文: " << chinese << endl;
os << "数学: " << math << endl;
os << "总分: " << total << endl;
return os;
}
};
int main()
{
struct Stu s = {"张三", 90, 80, 170};
s << cout;
return 0;
}
3.递增运算符重载
#include <iostream>
using namespace std;
struct Stu
{
int a;
//前置++重载
Stu& operator++()
{
a++;
return *this;
}
//后置++重载
Stu operator++(int)
{
Stu s=*this;
a++;
return s;
}
}s;
//重载输出运算符
ostream& operator<<(ostream& os,const Stu& s)
{
os << s.a << endl;
return os;
}
void test1()
{
cout << ++s;
}
void test2()
{
cout << s++;
}
int main()
{
s.a=1;
test1();
s.a=1;
test2();
return 0;
}
在自增运算符的重载中许多思想是和之前一样的,区别在于前置运算符重载作为成员函数时没有额外参数,后置运算符重载作为成员函数时需要一个额外的、类型为int的占位参数。这个参数通常不使用,只是用来与前置运算符区分开来。还有一个注意的点是,在前置++重载时,我们把返回值的类型设为了结构体的引用,这是因为当我们对内置数据连续自增操作是对该数据连续自增的,说起来很奇怪,其实就是进行一次自增操作,变量就自增一次。但如果在这里我们不使用引用,就会返回一个结构体类型的临时变量,如果在进行自增操作,对于原来的结构体变量是没有影响的。而后置++是不会进行连续操作的,所以我们不需要考虑。
4.关系运算符重载
#include <iostream>
#include <string>
using namespace std;
struct Stu
{
string name;
int age;
bool operator==(Stu& s)
{
if(this->name==s.name&&this->age==s.age)
return true;
else
return false;
}
};
int main()
{
Stu s1={"Tom",12};
Stu s2={"Tom",12};
if(s1==s2)
cout << "same" << endl;
return 0;
}
三、结构体排序 - sort
//版本1
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
//void sort(开始位置,结束位置);
//first:指向要排序范围的第⼀个元素的迭代器或者指针。
//last:指向要排序范围的最后⼀个元素之后位置的迭代器或者指针。
//版本2
template <class RandomAccessIterator, class Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare
comp);
//void sort(开始位置,结束位置,⾃定义排序函数);
//first:指向要排序范围的第⼀个元素的迭代器或者指针。
//last:指向要排序范围的最后⼀个元素之后位置的迭代器或者指针。
//comp:是⼀个⽐较函数或者函数对象
//这⾥开始位置和结束位置,可以是指针,也可以是迭代器
//⾃定义排序函数 可以是函数,也可以是仿函数
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int arr[] = { 4,5,6,9,7,1,2,8,5,4,2 };
int size = sizeof(arr) / sizeof(arr[0]);
//起始位置和结束位置传的是地址
sort(arr, arr + size);
for (int i = 0; i < size; i++)
{
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int main()
{
string s("defxxxabccba");
sort(s.begin(), s.end());
cout << s << endl;
return 0;
}
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare
comp);
#include <iostream>
#include <algorithm>
using namespace std;
//自定义一个比较函数,这个⽐较函数能够⽐较被排序数据的2个元素大小
//函数返回bool类型的值
bool compare(int x, int y)
{
return x > y;//排降序
}
int main()
{
int arr[] = { 4,5,6,9,7,1,2,8,5,4,2 };
int size = sizeof(arr) / sizeof(arr[0]);
//将函数名作为第三个参数传⼊sort函数中
sort(arr, arr + size, compare);
for (int i = 0; i < size; i++)
{
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
当然第三个参数还可以使用结构体中重载()运算符-仿函数。
#include <iostream>
#include<algorithm>
using namespace std;
//仿函数⽅式 - 仿函数也叫函数对象
struct Cmp
{
bool operator()(int x, int y)
{
return x > y;//排降序
}
}cmp;
int main()
{
int arr[] = { 4,5,6,9,7,1,2,8,5,4,2 };
int size = sizeof(arr) / sizeof(arr[0]);
//将结构体对象作为第三个参数传⼊sort函数中
sort(arr, arr + size, cmp);
for (int i = 0; i < size; i++)
{
cout << arr[i] << " ";
}
cout << endl;
return 0;
}
四、类
class tag
{
public:
成员变量列表;
成员函数列表;
}; // ⼀定要注意后⾯的分号
class Stu
{
public:
void init_stu()
{
name= "⼩明";
chinese = 0;
math = 0;
total = 0;
}
string name; //名字
int chinese; //语⽂成绩
int math; //数学成绩
int total; //总成绩
};
int main()
{
Stu s1; //创建类对象s1
Stu s2; //创建类对象s2
return 0;
}
(2)调用类对象成员
#include <iostream>
using namespace std;
int main()
{
//创建类对象
Stu s1;
//调⽤成员变量
s1.name = "张三";
s1.chinese = 90;
s1.math = 98;
s1.total = s1.chinese + s1.math;
Stu s2;
//调⽤成员函数
s2.init_stu();
return 0;
}
3.访问权限控制
#include <iostream>
using namespace std;
class Stu
{
public: //将成员函数设置为公有属性
void init_stu()
{
name= "⼩明";
chinese = 0;
math = 0;
total = 0;
}
private: //将成员变量设置为私有属性
string name; //名字
int chinese; //语⽂成绩
int math; //数学成绩
int total; //总成绩
};
int main()
{
Stu stu;
stu.init_stu(); //访问公有成员函数
cout << stu.name << endl; //访问私有成员变量,编译器报错
return 0;
}
4.结构体和类的区别