chapter-6

C++ Primer第六章!

#include "stdafx.h" 
#include<iostream>
#include<string>
#include<vector>
#include<initializer_list>          //实参数量未知,但是类型相同
#include<cassert>               //调式头文件,assert和NDEBUG

using namespace std;
int fact_sample(int val)
{
    int ret = 1;
    while (val > 1)
    {
        ret = ret*val;
        --val;
    }
    return ret;
}
int fact_iterator(int val)
{
    int tmp = val;
    if(val>1)
        return tmp*fact_iterator(--val);            //此处不能使用val*fact_iterator(--val),val的值会改变,造成表达式逻辑错误!
    else
    {
        return val;
    }
}

int factorial(int val)
{
    if (val>1)
        return val*fact_iterator(val-1);            //同int fact_iterator(int val)。
    else
    {
        return 1;
    }
}

int fact_for(int val)
{
    int ret = 1;
    for (int i = 1; i <= val; ++i)
    {
        ret = ret*i;
    }
    return ret;
}

size_t count_calls()
{
    static size_t ctr = 0;                      //局部静态对象,直到程序终止才被销毁
    return ++ctr;
}
void reference_fun(int &val)
{
    --val;
}
void p_fun(int *ip)
{
    *ip = *ip - 1;
}

bool isShorter(const string &s1, const string &s2)          //比较两个string对象的长度,string可能非常长,建议使用引用!
{                                                           //有的类型不支持拷贝(iostream,其同时也不支持const),则只能通过引用传递!
    return s1.size() < s2.size();
}

void print_arry(const int *arry)                            //等价于const int[],const int[10].不允许拷贝数组,但是形参还是可以写成数组形式,其实质是const int*
{
    cout << arry[0] << endl;
}

void print_char(const char *cp)
{
    if (cp)
        while (*cp)
            cout << *cp++;
}

void print_iter(const int *beg, const int *end)
{
    while (beg != end)
        cout << *beg++ << endl;
}

void print_count(const int ia[], size_t size)
{
    for (size_t i = 0; i != size; ++i)
    {
        cout << ia[i] << endl;
    }
}

void print_reference(int(&arr)[2])
{
    for (auto elem : arr)
        cout << elem << endl;
}

void print_mulit_ptr(int(*matptr)[2],int rowsize)       //指向含有10个整数的数组的指针。而int *matptr[10]表示10个指针构成的数组
// 等价于void print_mulit_ptr(intmatptr[][2],int rowsize)
{
    for (int i = 0; i < rowsize; ++i)
    {
        auto *beg = begin(*matptr);                     //由于已知数组的大小,也可以直接使用。
        while (beg!=end(*matptr))
        {
            cout << *beg << "+";
            ++beg;
        }
        cout << endl;
        ++matptr;
    }
}

void print_msg(initializer_list<string> lst)
{
    for (auto beg = lst.begin(); beg != lst.end(); ++beg)       //可以使用范围for循环处理其中的元素
        cout << *beg << " ";
    cout << endl;
}

const string &shortString(const string &s1, const string &s2)
{
    return s1.size() <= s2.size() ? s1 : s2;
}

void print_ret()
{
    return;
}

char &get_value(string &str, string::size_type ix)
{
    return str[ix];
}

vector<string> process_return()
{
    return { "aaa","bbb","ccc" };           //也可以返回renturn {};返回指定类型空vector
}

int odd[2] = { 13,14 };
int even[2] = { 52,93 };
decltype(odd) *arrptr(int i)
{
    return (i % 2) ? &odd : &even;          //int (*p)[2]=&odd;指向10个整数的数组的定义。
}

void print_overload()
{
    cout << "Test" << endl;;
}
void print_overload(const char *cp)
{
    cout<<*cp<<endl;
}
void print_overload(const char *cp,string::size_type sz)
{
    for (unsigned int i = 0; i < sz; ++i)
    {
        cout << cp[i];
    }
    cout << endl;                               //最好不在函数中定义输出的格式。
}

const string &shorterString(const string &s1, const string &s2)
{
    return s1.size() < s2.size() ? s1 : s2;
}
string &shorterString(string &s1, string &s2)
{
    auto &r = shorterString(const_cast<const string&>(s1), const_cast<const string&>(s2));
    return const_cast<string&>(r);
}

using sz = string::size_type;// size_t是全局定义的类型;size_type是STL类中定义的类型属性,用以保存任意string和vector类对象的长度!数组下表是size_t,而容器是size_type!
string screen(sz ht = 24, sz wid = 80, char background = ' ')       //一旦某个形参被赋予了默认值,他后面的所有形参都必须有默认值。
{
    return " ";                                                             //如果进行多次声明默认实参函数,则函数的后续声明只能为没有默认实参的形参添加默认值,且该形参右侧所有形参都必须有默认值!
}

inline const int &max_numbers(const int &a, const int &b)
{
    return a < b ? b : a;
}

constexpr size_t arr_count(size_t cnt)
{
    return 10 * cnt;
}

int addb(const int &a, const int &b)
{
    return(a + b);
}
int(*p_addb)(const int &a, const int &b);       //p_addb指向函数的指针,其中该函数的两个参数为const int的引用,返回值为int。

void  print_addb(ostream &os, int(*print_addb)(const int &a, const int &b),const int a=3,const int b=4)
{
    os << (*p_addb)(a,b) << endl;
}

int main()
{
    int j_sample = fact_sample(5);              //调用函数,实参可以隐式转换为形参!(形参初始化的机理和变量初始化一样)
    cout << j_sample << endl;                   //函数的返回类型不能是数组和函数,但可以是指向数组或函数的指针
    int j_iterator = fact_iterator(5);          //在头文件中放函数声明,在源文件中包含头文件,并定义函数。
    cout << j_iterator << endl;
    int j_for = fact_for(5);
    cout << j_for << endl;

    int j_reference = 5;
    reference_fun(j_reference);                 //引用传递,形参初始化的机理和变量初始化一样
    cout << j_reference << endl;

    int j_p = 5;                                //指针传递,形参初始化的机理和变量初始化一样
    p_fun(&j_p);
    cout << j_p << endl;

    //void fcn(const int i){}
    //void fcn(int i){}             两个函数是一样的,在形参拷贝初始化时,忽略了它的顶层const,因此给函数形参是一样的!
    //C++允许通过字面值初始化常量引用,而不允许初始化普通引用!

    int i_arry[10] = { 0,1,2,3 };
    print_arry(i_arry);

    //使用数组时确保不会越界,常见3种常用的技术
    
    //数组本身包含一个结束标记,例如C风格字符串
    char *p_char = "abc";
    print_char(p_char);

    //begin和end函数
    int j_arry[2] = { 1,2 };
    print_iter(begin(j_arry), end(j_arry));

    //显示传递一个数组大小
    int j_count[2] = { 9,8 };
    print_count(j_count, end(j_count) - begin(j_count));

    //数组引用形参
    int j_refer[2] = { 7,6};
    print_reference(j_refer);

    //传递多维数组
    int j_mulit_arry[2][2] = { 1,2,3,4 };
    print_mulit_ptr(j_mulit_arry, 2);
    
    //可变形参的函数,1、如果实参类型相同可以使用initializer_list;2、可变参数模版?
    //initializer_list<T> lst;
    //initializer_list<T> lst{a,b,c};   lst对象中的元素永远是常量值,即abc为const。
    //lst2=lst;                         拷贝或赋值一个initializer_list对象不会拷贝列表中的元素,原始列表和副本共享元素。
    //lst.size();
    //lst.begin();                      返回指向lst中首元素的指针。
    //lst.end();
    print_msg({ "aa","bb","cc" });      //向initializer_list形参中传递一个值的序列,必须把序列放在一对花括号内。

    //返回return
    //无返回值函数,可以使用return语句提前退出函数
    print_ret();

    //有返回值函数,返回类型应与函数类型一致。返回一个值的方式和初始化一个变量或形参的方式完全一样!
    string s_short1 = "aaa", s_short2 = "bbbb";
    cout << shortString(s_short1, s_short2) << endl;

    //返回局部变量时,则返回的是局部变量的副本(未命名临时变量)。请一定不要返回局部对象的引用或指针!!!

    //引用返回值,返回引用的函数得到左值,其他返回类型得到右值
    string s_value("aaaaa");
    get_value(s_value, 2) = 'B';
    cout << s_value << endl;

    //列表初始化返回值
    vector<string> err_msg_return = process_return();

    //主函数main的返回值
    //mian函数的返回值可以看作状态指示器,返回0表示执行成功,返回其他值表示失败。
    //cstdlib中定义了两个预处理变量,EXIT_FAILURE,EXIT_SUCCESS。

    //递归,函数调用自身,递归函数
    cout << factorial(5) << endl;

    // 返回数组指针
    using arrT = int[2];    //等价于typedef int arrT[10]
    //arrT* func(int i),等价于int (*func(int i))[10],也可以使用C++新规定:位置返回类型
    //auto func(int i) -> int(*)[10];
    //如果已知函数返回的指针将指向哪个数组,可以使用decltype推断
    int i_decl = 2;
    int (*out_decl)[2] = arrptr(i_decl);
    for (int i = 0; i < 2; ++i)
    {
        cout << (*out_decl)[i] << " ";          //int (*p)[2]=&odd;p为指针,指向10个整数的数组。
    }
    cout << endl;

    //重载函数,在形参数量或类型上有所不同。尽量只重载那些确实非常相似的操作
    const char *p_overload = "abcdefg";
    print_overload();
    print_overload(p_overload);
    print_overload(p_overload, 3);
    //顶层const形参,函数等价。底层const,函数可重载。顶层const只决定了在函数体内是否可以改变形参值,而不影响传入的实参。
    //int lookup(int),int lookup(const int);    int lookup(int*),int lookup(int *const),顶层const等价
    //int lookup(int&),int lookup(const int&);  int lookup(int*),int lookup(const int*),如果形参是某种类型的指针或引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载(底层const)。

    //const_cast和重载
    //shorterString,有什么特别作用吗?当使用非常量实参,想得到非常量的引用时,此时使用shorterString,定义2个重载函数。同时,重载函数应该避免强制类型转换!

    //默认实参,调用含有默认实参的函数,可以包含该实参,也可以省略该实参。
    string window;
    window = screen();
    window = screen(255, 255, '+');
    //window=screen(,,'+')  错误行为,在设计默认实参函数时,尽量让使用默认值的形参放在后面,不适用默认值的形参放在前面!
    
    //默认实参初始值含义?

    //内联函数,适用于节省开销、流程直接、频繁调用的函数
    int inline_a = 3, inline_b = 5;
    cout << max_numbers(inline_a, inline_b)<<endl;              //等价于cout<<(a < b ? b : a)<<endl;
    
    //constexpr函数,函数的返回类型和所有形参的类型都得是字面值类型(内置类型、引用、指针),而函数体中必须有且只有一条return语句。
    int arr_constexpr1[arr_count(2)];
    size_t i_constexpr = 2;
    //int arr_constexpr2[arr_count(i_constexpr)];           arr_count返回值不为常量表达式。

    //调试帮助,assert和NDEBUG
    assert(3 > 2);                                          //如果表达式为假,则输出信息并终止程序,可以用#define语句定义NDEBUG语句,关闭调式状态!

    //函数指针,指向函数而非对象
    int a_func = 1, b_func = 2;
    p_addb = addb;                                          //等价于p_addb=&addb.另外可以为p_addb赋值nullptr,表示没有指向任何一个函数。
    cout << addb(a_func, b_func) << endl;
    cout << p_addb(a_func, b_func) << endl;
    cout << (*p_addb)(a_func, b_func) << endl;              //等价于上面调用语句
    //使用函数指针作为形参, 将函数作为实参
    print_addb(cout, addb);
    print_addb(cout, addb,1,9);
    //返回指向函数的指针
    using F = int(int);
    using PF = int(*)(int);
    //PF f1(int),f1返回指向函数的指针
    //F *f1(int),f1返回指向函数的指针
    //auto f1(int)->int(*)(int),返回指向函数的指针。
    //也可以使用decltype推断函数的类型,得到形如F形式,接着声明函数。

    cin.ignore();

    return 0;
}

//函数包含返回类型声明、函数名、形参、实参、函数体等等部分,另外可以对函数进行重载。
//内联函数可以避免常见的函数调用开销!
//省略符形参是什么鬼?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容