算法学习入门---结构体和类(C++)

目录

1.结构体类型的声明、创建与使用(结构体嵌套+成员变量)

2.结构体类型的成员函数(重点)

3.操作符重载

4.sort函数和结构体的排序(重点)

1)数组排序

2)字符串排序

3.1)自定义排序(排序函数)

3.2)自定义排序(仿函数)

3.3)自定义函数(匿名函数)

4)结构体排序

5.类的声明、创建与使用

访问限制修饰符的限制层级:


1.结构体类型的声明、创建与使用(结构体嵌套+成员变量)

cpp 复制代码
#include<iostream>
#include<string>
using namespace std;

struct Stu{
	string name;
	int age;
	int total;
}s1,s2[10];

int main()
{
	struct Stu s3;
    Stu s4;
    Stu s5[10];
	return 0;
}

代码重点解释:

  • s3与s1的创建是同样效果的,只是一个是直接在结构体声明后直接创建,一个是初始化创建
  • s2[10]表示说创建了一个Stu结构体数组,可以看作是由10个Stu类型的变量所组成的数组;同时,也可以通过初始化创建结构体数组s5
  • 在C++中,初始化创建结构体变量时struct可以被省略,这与C语言是截然不同的
  • 声明后可以同时创建零个或多个变量(数组类型、指针类型都可),多个变量之间用逗号隔开,并且声明后创建的变量都为全局变量(在main函数外创建的)
  • 注意事项:结构体变量的名字和结构体类型的名字不能相同,声明的结构体类型不占用内存,创建完结构体变量后才会申请内存
cpp 复制代码
#include<iostream>
#include<string>
using namespace std;

struct Stu{
	string name;
	int age;
	int total;
}s1;

int main()
{
	s1 = {"zhangsan",20,100};
	Stu s2 = s1;
	cout<<s1.name<<" "<<s2.name<<endl;
	Stu s3;
	cin>>s3.name>>s3.age>>s3.total;
	cout<<s3.name<<s3.age;
	return 0;
}

代码重点解释:

  • s1的初始化可以如上代码所示,有点类似于数组的初始化,分别给3个成员变量3个值
  • s2 = s1 相当于把 s1 所有内容copy给了 s2;s2.name = s1.name 相当于只把s1的名字变量copy给s2的名字变量
  • 结构体变量里的成员变量也可以通过cin输入
cpp 复制代码
#include<iostream>
#include<string>
using namespace std;

struct score{
	int chinese;
	int math;
	int english;
};

struct stu{
	string name;
	score sc;
	int total;
};


int main()
{
	stu s1 = {"zhangsan",{100,80,90},0};
	cout<<s1.sc.chinese;
	return 0;
}

结构体的嵌套使用就是结构体某个成员变量为结构体,所以就是在使用结构体的结构体成员的成员变量

2.结构体类型的成员函数(重点)

成员函数是c++区别于c语言的一种特殊写法,c++结构体会有一些默认的成员函数,如构造函数、析构函数(可以看作初始化函数与自动销毁函数)等,这些函数都会被自动调用,不需要手动调用;当然,这些默认的构造函数与析构函数也可以自定义声明,但自动调用的性质不会改变

除了默认的成员函数,还可以自定义一些成员函数,这些成员函数可以有也可以没有;它们可以直接访问成员变量,调用时也是用 . 操作符

cpp 复制代码
#include<iostream>
#include<string>
using namespace std;

struct score{
	int chinese;
	int math;
	int english;
	//构造函数 
	score(int c,int m,int e)
	{
		chinese = c;
		math = m;
		english = e;
	}
	score()
	{
		cout<<"函数重载进行中"<<endl; 
	}
	//析构函数
	~score()
	{
		cout<<"程序结束"<<endl;	
	} 
	//score(int c,int m,int e);
	//score(int c,int m,int e):chinese(c),math(m),english(e){}
};

//score::score(int c,int m,int e)
//{
//	chinese = c;
//	math = m;
//	english = e;
//}


int main()
{
	{
		score s1(100,30,40);
		cout<<s1.chinese<<endl;
	}
	score s2;
	return 0;
}

代码重点解释:

  • 构造函数可以看作是创建出一个结构体对象后,直接给到结构体对象3个值,这3个值赋值给了3个成员对象;如果没有这样的一个构造函数,那就相当于跳过了构造函数进行成员对象的赋值
  • 析构函数指的是在一个结构体对象的生命周期结束时,自动销毁或手动销毁以前进行一系列的操作,这些操作就写在了析构函数中
  • 构造函数的名字为该函数所在结构体的结构体名称,不需要声明返回类型;析构函数的名称同构造函数,需要在函数名前加上一个 ~
  • 可以选择先在结构体中声明构造函数、析构函数或自定义函数,然后在结构体的外部进行定义(注释代码)
  • score(int c,int m,int e):chinese(c),math(m),english(e){} 构造函数的第二种写法,函数名+参数声明+成员变量的直接赋值+构造函数内部操作,一个也不能省略(全都要有!!!)
  • 注意:构造函数可以重载,析构函数不可以重载!!!
cpp 复制代码
#include<iostream>
#include<string>
using namespace std;

struct score{
	int chinese;
	int math;
	int total;
	void init_sc()
	{
		chinese = 100;
		math = 90;
		total = chinese + math;
	}
	void print()
	{
		cout<<total;
	}
}s1;


int main()
{
	s1.init_sc();
	s1.print();
	return 0;
}

自定义了一个成员函数,成员函数中直接调用了成员变量进行赋值,创建完结构体变量s1后,调用完该成员函数后,再调用print成员函数(打印total成员变量的函数),打印出来为190

3.操作符重载

cpp 复制代码
#include<iostream>
#include<string>
using namespace std;

struct score{
	int chinese;
	int math;
	int english;
};

ostream& operator<<(ostream& os,struct score& s)
{
	os<<s.chinese<<" "<<s.english<<" "<<s.math;
    return os;
}

int main()
{
	score s1={100,90,80};
	cout<<s1<<endl;
	return 0;
}

对输出流运算符进行了操作符重载,c++中每个操作符的底层是一个函数,例如<<操作符的函数返回类型为ostream&,参数为输出流以及输出对象;操作符重载相当于给予<<一个新的函数定义,让他具备新的功能,这样输出流操作符就能一次性把结构体对象的所有内容都打印了

cpp 复制代码
#include <iostream>
using namespace std;

class Time {

    public:
        int hours;      // 小时
        int minutes;    // 分钟

        Time() {
            hours = 0;
            minutes = 0;
        }

        Time(int h, int m) {
            this->hours = h;
            this->minutes = m;
        }

        void show() {
            cout << hours << " " << minutes << endl;
        }

        // write your code here......
        Time operator+(Time &t)
        {   Time sb;
            sb.hours=this->hours+t.hours;
            sb.minutes=this->minutes+t.minutes;
            if(sb.minutes>=60) 
            { int i=0;
                sb.hours+=sb.minutes/60;
                ++i;
                sb.minutes=sb.minutes-60*i;
            }
            return sb;
        }

};

int main() {

    int h, m;
    cin >> h;
    cin >> m;

    Time t1(h, m);
    Time t2(2, 20);

    Time t3 = t1 + t2;
    t3.show();
    
    return 0;
}

当我们需要对某个类创建出来的对象,或者某个结构体创建出来的变量,+-×÷进行运算符重载时,需要在类、结构体的定义里进行

先定义了一个变量sb,先调用了对象自身(this指针),然后调用了另外一个对象;让自己的时间、分钟进行一些操作以后给到变量sb,然后返回以后即完成操作符重载

cpp 复制代码
#include <iostream>

class Complex {
private:
    double real, imag;
public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
    
    void display() const {
        std::cout << real << " + " << imag << "i" << std::endl;
    }
    
    // 声明为友元,让全局函数能访问私有成员
    friend Complex operator-(const Complex& c);
    friend Complex& operator++(Complex& c);      // 前置++
    friend Complex operator++(Complex& c, int);  // 后置++
    friend bool operator!(const Complex& c);
};

// 全局重载负号运算符
Complex operator-(const Complex& c) {
    return Complex(-c.real, -c.imag);
}

// 全局重载前置自增运算符
Complex& operator++(Complex& c) {
    ++c.real;
    ++c.imag;
    return c;
}

// 全局重载后置自增运算符
Complex operator++(Complex& c, int) {
    Complex temp = c;
    c.real++;
    c.imag++;
    return temp;
}

// 全局重载逻辑非运算符
bool operator!(const Complex& c) {
    return (c.real == 0 && c.imag == 0);
}

int main() {
    Complex c1(3, 4);
    Complex c2 = -c1;  // 使用全局重载的负号运算符
    c2.display();      // 输出: -3 + -4i
    
    ++c1;              // 使用全局重载的前置++
    c1.display();      // 输出: 4 + 5i
    
    Complex c3 = c1++; // 使用全局重载的后置++
    c3.display();      // 输出: 4 + 5i
    c1.display();      // 输出: 5 + 6i
    
    Complex c4(0, 0);
    if (!c4) {         // 使用全局重载的逻辑非
        std::cout << "c4 is zero" << std::endl;
    }
    
    return 0;
}

在全局进行运算符重载也是可行的,声明完要重载的操作符以后,声明为友元,让全局函数能够访问私有成员

4.sort函数和结构体的排序(重点)

1)数组排序

  • 如上图所示,sort函数是对[first,last)区间进行排序,last、first都是指数组指针
  • sort函数在algorithm头文件中
  • 数组名就是首元素地址,即为first;因为左闭右开,所以last需要是数组最后一个元素的后一个地址;从first到last之间的元素个数应该是 sizeof(arr)/sizeof(a[0])
cpp 复制代码
#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
	int arr[10]={3,4,1,5,9,10,3,8,19,-1};
	int sz = sizeof(arr)/sizeof(arr[0]);
	sort(arr,arr+sz);
	for(int i=0;i<sz;i++) cout<<arr[i]<<" ";
	return 0;
}

2)字符串排序

字符串排序可以用到begin、end函数

cpp 复制代码
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;

int main()
{
	string st = "ahasdnjdbadsb";
	sort(st.begin(),st.end());
	cout<<st;
	return 0;
}

3.1)自定义排序(排序函数)

  • sort函数有3个参数,起始位置、终止位置和bool类型的排序函数
  • 排序函数默认为less函数(升序排序),可以切换成greater函数,sort的less、greater函数()不能省,priority_queue的less、greater函数()必须省
  • 排序函数接收两个参数(每次用来比较的2个参数),并返回一个布尔值。
  • 排序函数的2个参数一开始的位置为第一个在前,第二个在后。排序函数为true时,参数位置不变;为false时,参数进行位置交换
cpp 复制代码
#include<iostream>
#include<algorithm>
using namespace std;

bool cmp(int x,int y)
{
	return x>y;
}

int main()
{
	int arr[10]={3,4,1,5,9,10,3,8,19,-1};
	int sz = sizeof(arr)/sizeof(arr[0]);
	sort(arr,arr+sz,cmp);
	for(int i=0;i<sz;i++) cout<<arr[i]<<" ";
	cout<<endl;
	sort(arr,arr+sz,less<int>());
	for(int i=0;i<sz;i++) cout<<arr[i]<<" ";
	return 0;
}

3.2)自定义排序(仿函数)

不同于priority_queue是对 < 进行操作符重载,来实现排序方式的改变;sort函数是可以通过一个结构体对象,这个对象包含了对()操作符的重载,来实现排序方式的改变

cpp 复制代码
#include<iostream>
#include<algorithm>
using namespace std;

struct Cmp{
	bool operator()(int x,int y)
	{
		return x>y;
	}
}cmp;

int main()
{
	int arr[10]={3,4,1,5,9,10,3,8,19,-1};
	int sz = sizeof(arr)/sizeof(arr[0]);
	sort(arr,arr+sz,cmp);
	for(int i=0;i<sz;i++) cout<<arr[i]<<" ";
	return 0;
}

3.3)自定义函数(匿名函数)

c++11标准下,创新了一个名为lambda表达式的东西,可以理解成简化表达的一个函数,这样的函数不需要规定函数返回类型,以下是匿名函数的一些表达式讲解

  • \] // 不捕获任何外部变量

  • =\] // 隐式按值捕获所有外部变量

  • =, \&x\] // 按引用捕获x,其余按值捕获

  • () //获取函数参数

  • \] 主要用来捕捉局部变量,全局变量与静态局部变量不需要捕捉可以直接访问

  • lambda表达式在C++99标准下不可用!!!

cpp 复制代码
#include<iostream>
#include<algorithm>
using namespace std;


int main()
{
	int arr[10]={3,4,1,5,9,10,3,8,19,-1};
	int sz = sizeof(arr)/sizeof(arr[0]);
	sort(arr,arr+sz,[](int x,int y)
	{
		return x>y;
	});
	for(int i=0;i<sz;i++) cout<<arr[i]<<" ";
	return 0;
}

4)结构体排序

cpp 复制代码
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;

struct Stu
{
	string name;
	int age;	
}arr[3]={{"zhangsan",20},{"lisi",35},{"wangwu",19}};

ostream& operator<<(ostream& os,Stu& s)
{
	os<<s.name<<" "<<s.age;
	return os;
}

bool cmp_s_byage(Stu s1,Stu s2)
{
	return s1.age<s2.age;
}

int main()
{
	int sz = sizeof(arr)/sizeof(arr[0]);
	sort(arr,arr+sz,cmp_s_byage);
	for(auto& st:arr) cout<<st<<endl;
	return 0;
}

根据年龄大小进行排序,依旧使用排序函数,只是需要把排序函数中的参数类型给更换以下即可

cpp 复制代码
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;

struct Stu
{
	string name;
	int age;	
}arr[3]={{"zhangsan",20},{"lisi",35},{"wangwu",19}};

ostream& operator<<(ostream& os,Stu& s)
{
	os<<s.name<<" "<<s.age;
	return os;
}

int main()
{
	int sz = sizeof(arr)/sizeof(arr[0]);
	sort(arr,arr+sz,[](Stu s1,Stu s2)
	{
		return s1.age>s2.age;
	});
	for(auto& st:arr) cout<<st<<endl;
	return 0;
}

也可以将排序函数给简化成一个匿名函数,这样代码能够更加简洁,仿函数也只是将参数类型切换即可,此处省略

5.类的声明、创建与使用

cpp 复制代码
class tag{
	public:
		//成员变量列表
		//成员函数列表 
};

类的声明与定义与struct类型一样,都可以包含成员变量、成员函数(类也有构造函数与虚构函数)

需要注意的是:结构体类型下,默认访问形式是public的;类下默认访问形式是private的

cpp 复制代码
#include<iostream>
using namespace std;

class tag{
	public:
		string name;
		int age;
};

int main()
{
	tag s1;//创建类tag的对象s1 
	s1.age = 20;
	s1.name = "zhangsan"; 
	return 0;
}

类创建出来的变量,我们也可以称其为一个对象

类的创建、使用与结构体类型也一样,只是在调用成员时的访问限制不同

访问限制修饰符的限制层级:

相关推荐
墨雪不会编程2 小时前
C++ string 详解:STL 字符串容器的使用技巧
java·开发语言·c++
yangpipi-3 小时前
《C++并发编程实战》第5章 C++内存模型和原子操作
android·java·c++
SunkingYang3 小时前
MFC进程间消息通信深度解析:SendMessage、PostMessage与SendNotifyMessage的底层实现与实战指南
c++·mfc·共享内存·通信·postmessage·sendmessage·进程间
XFF不秃头4 小时前
力扣刷题笔记-旋转图像
c++·笔记·算法·leetcode
王老师青少年编程4 小时前
csp信奥赛C++标准模板库STL案例应用3
c++·算法·stl·csp·信奥赛·lower_bound·标准模版库
有为少年5 小时前
Welford 算法 | 优雅地计算海量数据的均值与方差
人工智能·深度学习·神经网络·学习·算法·机器学习·均值算法
Tim_105 小时前
【C++入门】04、C++浮点型
开发语言·c++
Ven%5 小时前
从单轮问答到连贯对话:RAG多轮对话技术详解
人工智能·python·深度学习·神经网络·算法