[- C++趣玩篇1 -] 从打印开始说起

4,325 阅读8分钟

C++是早晚要过的坎,为了JNI,还是稍微拾起来一些吧。

我想用最简单的事物,讲述最复杂的道理,用最有趣的方式,引导最无聊的你。


前言:本故事纯属虚构

背景1988年,龙少参加星宇国际的面试,因为打印出了"Hello, World!"而震惊全场,立刻得到offer.

林兮,20岁(选择困难症,需求改不断)
龙少,20岁(会打印输出语句的初级工程师)
捷特,21岁(拥有面向对象的思想中级工程师)


1.打印语句

捷特:龙少,C++学的怎么样?
龙少:感觉很不错,现在我都能打印Hello,World了
捷特:不错不错,再接再厉

#include <iostream>

int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}
---->[打印]----
Hello, World!

林夕:用C++给我画个脸
龙少:给你一个嘟嘴的脸

int main() {
    std::cout << "----------" << std::endl;
    std::cout << "| -    - |" << std::endl;
    std::cout << "|  .   . |" << std::endl;
    std::cout << "|    -}  |" << std::endl;
    std::cout << "----------" << std::endl;
    return 0;
}

林夕:不怎么好看,把-改成~,我来瞅瞅
龙少:好嘞,这简单,一个个字符改一下就OK了

int main() {
    std::cout << "~~~~~~~~~~" << std::endl;
    std::cout << "| ~    ~ |" << std::endl;
    std::cout << "|  .   . |" << std::endl;
    std::cout << "|    -}  |" << std::endl;
    std::cout << "~~~~~~~~~~" << std::endl;
    return 0;
}

林夕:算了,改回来吧,这样好搓
龙少:你莫不是在玩老子?一个个字符改一下...TM真麻烦

int main() {
    std::cout << "----------" << std::endl;
    std::cout << "| -    - |" << std::endl;
    std::cout << "|  .   . |" << std::endl;
    std::cout << "|    -}  |" << std::endl;
    std::cout << "----------" << std::endl;
    return 0;
}

林夕:我仔细想了一下,从美学的角度,上用波浪,下用直线,眉毛也用波浪会更好。
龙少:老哥你到底想咋样,改来改去,很累的好嘛。
林夕:还不错,先这样,去午休吧,下午再说。

int main() {
    std::cout << "~~~~~~~~~~" << std::endl;
    std::cout << "| ~    ~ |" << std::endl;
    std::cout << "|  .   . |" << std::endl;
    std::cout << "|    -}  |" << std::endl;
    std::cout << "----------" << std::endl;
    return 0;
}

龙少中午愤愤不平地将这件事告诉捷特
捷特:如果是上下一百行,你还能一个个改吗?
龙少:如果这样,我只写一遍,谁让我改,我跟谁急。
捷特:哎,年轻人就是心浮气躁。我来教你提取变量和for循环


2.for循环

在捷特的指导下,龙少将代码用for循环重构

#include <iostream>
using namespace std;

int main() {
    auto top="~";
    for (int i = 0; i < 10; ++i) {
        i!=9?cout << top: cout << top<<endl;
    }
    std::cout << "| ~    ~ |" << std::endl;
    std::cout << "|  .   . |" << std::endl;
    std::cout << "|    -}  |" << std::endl;
    std::cout << "~~~~~~~~~~" << std::endl;
    return 0;
}

龙少:虽然正确,但看着有点糟心,感觉比我的代码复杂了好多啊。
捷特:简单必有简单的成本,复杂必有复杂的价值。 如果林夕再让你改上面,你只要改一处就行了。

int main() {
    auto top="#";
    //英雄所见...
}

龙少:咦,果然,这样就少改9次,爽歪歪。
捷特:那下面你也照样子改一下。

虽说龙少是个萌新,但理解力还是非常好的。秒写:

int main() {
    auto top="#";
    auto bottom="-";
    for (int i = 0; i < 10; ++i) {
        i!=9?cout << top: cout << top<<endl;
    }
    std::cout << "| ~    ~ |" << std::endl;
    std::cout << "|  .   . |" << std::endl;
    std::cout << "|    -}  |" << std::endl;
    for (int i = 0; i < 10; ++i) {
        i!=9?cout << bottom: cout << bottom<<endl;
    }
    return 0;
}

甚至这小子还能举一反三:

int main() {
    auto top="#";
    auto bottom="-";
    auto brow="~";
    auto eyes="X";
    for (int i = 0; i < 10; ++i) {
        i!=9?cout << top: cout << top<<endl;
    }
    cout << "|  "<< brow <<"   "<< brow <<" |" << endl;
    cout << "|  "<< eyes <<"   "<< eyes <<" |" << endl;
    cout << "|    -}  |" << endl;
    for (int i = 0; i < 10; ++i) {
        i!=9?cout << bottom: cout << bottom<<endl;
    }
    return 0;
}

捷特:优秀如你,一看就是学编程的料,不过给你一点建议,勤加注释。
龙少:好,老哥说加,我就加。虽然已经面目全非,但是真的挺好用啊,下午我可以不用怕林夕了。
捷特:呵呵,你太小瞧产品的需求力了。


3.函数封装

捷特:如果打印两个脸该怎么办?
龙少:对于CV工程师,这易如反掌。

int main() {
    auto top="#";//顶部
    auto bottom="-";//底部
    auto brow="~";//眉毛
    auto eyes="X";//眼睛
    //打印第一个脸
    for (int i = 0; i < 10; ++i) {
        i!=9?cout << top: cout << top<<endl;
    }
    cout << "|  "<< brow <<"   "<< brow <<" |" << endl;
    cout << "|  "<< eyes <<"   "<< eyes <<" |" << endl;
    cout << "|    -}  |" << endl;
    for (int i = 0; i < 10; ++i) {
        i!=9?cout << bottom: cout << bottom<<endl;
    }
    //打印第二个脸
    for (int i = 0; i < 10; ++i) {
        i!=9?cout << top: cout << top<<endl;
    }
    cout << "|  "<< brow <<"   "<< brow <<" |" << endl;
    cout << "|  "<< eyes <<"   "<< eyes <<" |" << endl;
    cout << "|    -}  |" << endl;
    for (int i = 0; i < 10; ++i) {
        i!=9?cout << bottom: cout << bottom<<endl;
    }
    return 0;
}


捷特:我们要有世界设计师的自尊,决不能称自己是CV工程师。
龙少:本来就是嘛,我们不就是将别的东西抄抄改改吗。
捷特无奈摇摇头:哎,现在的年轻人啊。这样,如果要打印两个脸不一样怎么办?
龙少:你是说再定义一组变量?
捷特无奈:那打印100个脸,你的代码还能不能看了?
龙少:100个...我选择死亡....
捷特:是时候让函数帮你了,它可以封装若干条语句,并且可以通过参数控制语句。

#include <iostream>

//声明函数
void printFace(const char *top, const char *bottom, const char *brow, const char *eyes);

using namespace std;

int main() {
    printFace("#", "-", "~", "X");
    printFace("O", "N", ".", "$");
    return 0;
}

/**
 * 打印一个脸
 * @param top 顶部
 * @param bottom 底部
 * @param brow 眉毛
 * @param eyes 眼睛
 */
void printFace(const char *top, const char *bottom, const char *brow, const char *eyes) {
    for (int i = 0; i < 10; ++i) {
        i!=9?cout << top: cout << top<<endl;
    }
    cout << "|  "<< brow <<"   "<< brow <<" |" << endl;
    cout << "|  "<< eyes <<"   "<< eyes <<" |" << endl;
    cout << "|    -}  |" << endl;
    for (int i = 0; i < 10; ++i) {
        i!=9?cout << bottom: cout << bottom<<endl;
    }
}

龙少:难以置信,竟然还有这种操作,调用printFace,传入四个字符就行了
捷特:现在有没有对C++有一点更深的认识?
龙少连连点头。下午面对林夕的百般刁难,龙少轻松搞定。
林夕纳闷:哥还治不了你,打印得太小了,给我放大一倍。
龙少:你怎么不让老鼠变大去抓猫。两人小吵一架,下班后不欢而散。


4.类的封装

捷特和龙少是邻居,也是从小到大的玩伴。两个一起坐公交回家。
龙少:你给评评理,是不是他刁钻了。
捷特:确实有点,不过这也确实是个需求。先不说这个,给你说说面向对象编程。
龙少:哎,单身狗一个,哪有机会面向对象编程。
捷特:不是那个对象,你看,我们现在在坐公交对吧,为什么不自己走?
龙少:十几里路,你傻啊,腿走断了,太麻烦。
捷特:为什么不自己造一个公交车,自己开?
龙少:成本啊,坐个公交三毛三,造个公交少说几十万,够坐一辈子了。
捷特:所以我们只要通过 bus.go(0.33),就可以很简单的到家,其实编程里也有这种思想,就是面向对象。如果有一个Facer的小伙伴,你只需要通过它的printFace()就可以打印出一个脸,你想不想认识他? 就像这样:

 #include "Facer.h"

using namespace std;

int main() {
    Facer face("#", "-", "~", "X");
    face.printFace();
    face.bottom="V";
    face.printFace();
    return 0;
}

龙少:感觉挺棒的,要的要的。快告诉我怎么实现?


首先定义一个头文件Facer.h,来说明Facer的属性(字段)和能力(方法)

#include <iostream>
using namespace std;

#ifndef TOLYC_FACER_H
#define TOLYC_FACER_H

class Facer {
public:
    Facer(const string &top="#", const string &bottom="#", const string &brow="~", const string &eyes=".");
    ~Facer();
public:
    string top;
    string bottom;
    string brow;
    string eyes;
public:
    void printFace() ;
};

#endif //TOLYC_FACER_H

Facer.cpp中实现构造、析构方法以及功能方法的实现。

#include "Facer.h"

Facer::Facer(
        const string &top,
        const string &bottom,
        const string &brow,
        const string &eyes) : top(top),
                              bottom(bottom),
                              brow(brow),
                              eyes(eyes) {}

void Facer::printFace() {
    for (int i = 0; i < 10; ++i) {
        i != 9 ? cout << top : cout << top << endl;
    }
    cout << "|  " << brow << "   " << brow << " |" << endl;
    cout << "|  " << eyes << "   " << eyes << " |" << endl;
    cout << "|    -}  |" << endl;
    for (int i = 0; i < 10; ++i) {
        i != 9 ? cout << bottom : cout << bottom << endl;
    }
}

Facer::~Facer() {

}

龙少:我明白了,这挺简单嘛,面向对象不过如此。
捷特:too yong too simple,这只是面向对象的冰山一角,以后有机会我跟你好好说说。这里用cout处理并不灵活,我们可以使用字符串进行拼接,再一次打印。

//
// Created by 张风捷特烈 on 2019/10/3.
//

#include <iostream>

using namespace std;

#ifndef TOLYC_FACER_H
#define TOLYC_FACER_H

class Facer {
public:
    Facer(const string &top="#", const string &bottom="#", const string &brow="~", const string &eyes=".");
    ~Facer();
public:
    string top;
    string bottom;
    string brow;
    string eyes;
public:
    void printFace() ;
    string getFace();
};

#endif //TOLYC_FACER_H

//
// Created by 张风捷特烈 on 2019/10/3.
//

#include "Facer.h"

Facer::Facer(
        const string &top,
        const string &bottom,
        const string &brow,
        const string &eyes) : top(top),
                              bottom(bottom),
                              brow(brow),
                              eyes(eyes) {}

void Facer::printFace() {
    cout<< getFace() << endl;
}

Facer::~Facer() {

}

string Facer::getFace() {
    string result;
    for (int i = 0; i < 10; ++i) {
        i != 9 ? result+=top : result+=top+"\n";
    }
    result+= "|  " +brow + "   " + brow + " |" +"\n";
    result+= "|  " +eyes + "   " + eyes + " |" +"\n";
    result+= "|    -}  |\n";
    for (int i = 0; i < 10; ++i) {
        i != 9 ? result+=bottom : result+=bottom+"\n";
    }
    return result;
}