C++如何设计函数多返回值以及如何加速输入输出效率

一、如何设计函数多返回值

有那么一种情况,函数本身需要返回多个值,如在地图参数中需要返回二维坐标(x,y),或者是系统设计中需要返回一个学生多门课程的成绩。这里提供了一些做法和思路。

1. 全局变量,为什么不用它?

如这样的方法,当我们需要通过函数对多个值进行返回和传递的时候,可以使用一种弄虚作假的方式,就是使用全局变量,不需要函数返回,只需要在关键时刻进行设置就可以了。

int x,y;
void getWay(int a,int b){
    x=a,y=b;
}
int main(){
    getWay(10,20);
    cout<<x<<" "<<y<<endl;
    return 0;
}

这是在自欺欺人,当我们需要多个值进行返回传递的时候,我们可以使用全局变量避免掉这个设计,但这并不能解决核心问题,使用全局变量当我们需要多次调用或者递归调用函数时则会出现诸如数据紊乱等的错误,而且一旦在工程中使用大量的全局变量,则会造成很多意外的后果,因此这里不过多讨论。

2. 双返回值,pair

在我们学习pair一对数据的时候我们就有了解,我们可以通过pair作为数据类型进行多组数据的传递,这往往对于两个数据(较少的数据)而言,是最理想的情况。

pair<string,int> getClass(int id){
    return make_pair("DOTCPP!",id);
}
  
int main(int argc,char **argv){
    pair<string,int> a;
    a=getClass(10);
    cout<<a.first<<" "<<a.second<<endl;
    return 0;
}

3. 指针返回法

指针返回法(又名数组返回法)顾名思义,我们的数据类型使用的是一个指针类型的数组作为返回类型,其返回的内容在内存空间上是连贯的,这个方法也被用来进行常规的数组作为参数的返回。

#include<iostream>
using namespace std;
int *getWay(int n){
    int *p=new int[3];
    p[0]=n+10;
    p[1]=n+20;
    p[2]=n+30;
    return p;
}
int main(){
    int *res = getWay(10);
    for(int i=0;i<3;i++){
        cout<<res[i]<<' ';
    }
    delete []p;//防止内存泄漏
    return 0;
}

4.结构体返回法

这个是对于超过两个元素的最推荐的写法了,对于指针返回法,其是相当于把多组的数据当成一个共同类型的参数作为处理,可一旦返回的元素同时有整形又有浮点型这样不同的元素,指针返回的方法往往就不太适用了,最理想的方式还是设计结构体,使用结构体返回法。

#include<iostream>
using namespace std;
struct ans
{
    int a;
    char b;
    double c;
};
ans getWay(int n){
    struct ans r_ans;
    r_ans.a=n;
    r_ans.b=n;
    r_ans.c=n+0.11;
    return r_ans;
}
int main(){
    ans ans = getWay(97);
    cout<<ans.a<<' '<<ans.b<<' '<<ans.c<<endl;
    return 0;
}
//output:
//97 a 97.11

这样有点类似于数据结构定义的内容,事实上的确是相通的,函数多返回值是C/C++设计上所缺失的,也由于时代原因当时的设计不需要考虑如今那么多,而如今的各种新型语言均支持了这个概念,直接使用逗号进行分割即可达到效果。

二、如何加速输入输出效率

1. 简介

遇到大数据时,往往读写文件成了程序运行速度的瓶颈,需要更快的读取方式。相信几乎所有的C++学习者都在cin机器缓慢的速度上栽过跟头,有很多案例中提供几个数据,却在后台测评却提供了近千,近万的数据量是常事,而很多人会发现,明明算法正确的问题,却总是在超时,但把自己的输入换成scanf,输出换成printf之后莫名其妙又可以通过了,于是便冷眼相对C++的cin与cout。

在C++中,cin与cout往往不需要我们手动设置格式而变得灵活,因此更趋向于我们便捷式的使用,但这并不是说cin与cout就一定比scanf和printf慢,我们可以通过C++输入输出流解除绑定的方式进行加速,使其提升至C语言scanf和printf般的速度。

2.实现原理

cin在为了与scanf保持同步,设置了一个缓冲区,为了保证各位混用两者的情况不会出错,利用这个缓冲区进行同步,不至于发生指针错误造成乱码,因此cin会牺牲一点点效率,而这一点点的效率,在大数据读取和运算的时候也会产生极大的影响,我们可以通过sync_with_stdio(false)的方式取消这个缓冲区,让cin变成和scanf一样的效率。

a) sync_with_stdio

这个函数是一个“是否兼容stdio”的开关,C++为了兼容C,保证程序在使用了std::printf和std::cout的时候不发生混乱,将输出流绑到了一起,默认情况为sync_with_stdio(ftrue),即开启。

b)cin.tie(0),cout.tie(0);

cin.tie(NULL);cout.tie(NULL);只解除的是C++运行库层面的对数据传输的绑定,stdin和stdout应该在更底层的操作系统层面有绑定,没有解除,也就是说,cin.tie(0)与cout.tie(0)的方式是继续松绑c++传输的效率。

因此我们可以在自己的代码中建立一个如此的模板:

#include<iostream>
using namespace std;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
  
    /*
        写上你想写入的代码,并使用cin,cout输入输出
     */
  
    return 0;
}

也可以用宏定义的方式简写这段代码:

#define jiasu ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);

在主函数进行引用即可。

我们了解了解除绑定的输入输出流,这会让我们的代码输出变得迅速,然而,对于输入而言,亦有快速读取这一个更优秀的方案(相对来说也较为麻烦)。

在我们学习C语言的时候,我们曾经学过字符的输入函数getchar(),她从标准输入里面读取下一个字符,相当于gets(stdin),返回类型为int型,为用户输入的ASCII码,出错则返回-1。

3.实现方式

将所读取的数据按位进行分段截取,比如读取入123这个数据,在确定所读取的是数字的情况下,先读取数字1,如果其后还有数据,将第一个读取的数据1先乘以10再读取第二个数据2,接着读取3,将前面的12再乘以10,最后就可以读取成功123,而对于负数而言,我们只需要设置一个flag标记,对第一位的读取进行一个特判即可,在读取完这些数据之后,将获取到的整体数据进行返回,这边就是快读的基本思想。

请记住这个核心:判断是否为数字à按位读取à转换ASCII码为数字à继续读取à进行乘10与相加à读取结束返回存储的值

这里有一个简单版本的快读

inline int read()
{
    int X=0; bool flag=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
    while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();} 
    if(flag) return X;
    return ~(X-1);
}

使用inline重载函数和重写read(),也可以去掉inline,只不过这样效率稍微慢一些,输入的使用方法就可以照做一般的函数进行使用即可。

int tmp=read();

std::cout<<tmp<<std::endl;

4.扩展,快速输出

通快速读取,亦有快读输出,其思想更为简单,效率也只是略微胜一筹,其思维是,按照每一位进行拆解输出,注意我们需要让在后面的数字后输出,拆解位时就务必要注意,因此,较为简单的方法就是模仿递归进行设计。

inline void write(int X)
{
    if(X<0) {X=~(X-1); putchar('-');}
    if(X>9) write(X/10);
    putchar(X%10+'0');
}

5. 推荐模板

使用C++的万能模板类并创建新的命名空间IO,可以手动指明所需要返回参数的类型,int还是long还是别的,这样的方式更加人性化,可以省去上面那样简单的方式必须手动修改函数类型的缺点,但是这样的模板却更加的复杂和繁长,不易快速上手使用,权且做一个理解。

#include<iostream>
namespace IO
{
    inline char nc() {
        static char buf[100000], *p1 = buf, *p2 = buf;
        return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
    }
    template<typename T>
    inline T read() {
        char ch = nc(); 
        T sum = 0;
        while (!(ch >= '0'&&ch <= '9'))
        {
            ch = nc();
            if (ch == EOF)  return EOF;
        }
        while (ch >= '0'&&ch <= '9')
        {
            sum = sum * 10 + ch - 48;
            ch = nc();
            if (ch == EOF)  return EOF;
        }
        return sum;
    }
}
using namespace IO;
  
int main(){
    long long tmp=read<long long>();    //输入方式
    std::cout<<tmp<<std::endl;
  
    while(tmp = read<long long>()!=EOF){} //循环判断EOF的方式
    return 0;
}

6.项目实训

到此为止关于STL的文章就结束了,我会在上面放一个关于学生信息管理系统的实训项目源码与设计需求,感兴趣的同学可以下载参考。

  • 21
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dola_Pan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值