第一部分 对象和作用域(C++ In Action学习总结)

1,全局作用域与局部作用域

#include <iostream>

using namespace std;

class World
{
public:
    World(int id)
            : _identifier(id)
    {
        cout << "Hello:" << _identifier << endl;
    }

    ~World()
    {
        std::cout << "Good Bye:" << _identifier << endl;
    }

private:
    const int _identifier;  // const 变量必须在前导时进行初始化,只有一次初始化的机会,后续再也不能改变
};

World theWorld(1);    // 全局作用域,将初始化和析构theWorld对象

int main()
{
    World worldSmall(2);
    for(int i = 3; i < 5; i++)
    {
        World aWorld(i);
    }
    return 0;
}

输出结果:

Hello:1		先初始化全局作用域
Hello:2		从上到下依次初始化
Hello:3
Good Bye:3
Hello:4
Good Bye:4
Good Bye:2
Good Bye:1

2, 嵌入对象(has-a)

#include <iostream>

using namespace std;

class Matter
{
public:
    Matter(int id) : _identifier(id)
    {
        cout << "Matter in:" << _identifier << endl;
    }

    ~Matter()
    {
        cout << "Matter leave:" << _identifier << endl;
    }

private:
    const int _identifier;
};

class World
{
public:
    World(int id) : _matter(_identifier), _identifier(id)
    {
        cout << "World in:" << _identifier << endl;
    }

    ~World()
    {
        std::cout << "World leave:" << _identifier << endl;
    }

private:
    const int _identifier;  // const 变量必须在前导时进行初始化
    const Matter _matter;   // 嵌入类型对象 matter
};

World theWorld(1);    // 全局作用域,将初始化和析构theWorld对象

int main()
{
    World worldSmall(2);
    return 0;
}

输出结果:

Matter in:1
World in:1
Matter in:2
World in:2
World leave:2
Matter leave:2
World leave:1
Matter leave:1
2.1,初始化顺序

数据成员按照他们在类定义中的出现顺序进行初始化,和前导顺序无关,上例中,因为_identifier在_matter前面出现,先初始化identifier,在初始化_matter。

3,继承

#include <iostream>

using namespace std;

class Book
{
public:
    Book(int size):_size(size)
    {
        cout << "Init book size is " << _size << endl;
    }

    ~Book()
    {
        cout << "Destroy book size is " << _size << endl;
    }

private:
    const int _size;     // 尺寸
};

class MathBook : public Book
{
public:
    MathBook(int size, string color):_color(color), Book(size)
    {
        cout << "Init MathBook Color is " << _color << endl;
    }
    ~MathBook()
    {
        cout << "Destroy MathBook Color is " << _color << endl;
    }

private:
    const string _color;    // 书的颜色
};

MathBook mb1(1,"yellow");
int main()
{
    MathBook mb2(2, "green");
    return 0;
}

3.1, 构造顺序

首先完全构造基类,再构造派生类。

前导初始化中的顺序是没有任何意义的。

派生类构造顺序一般是:嵌入体 => 构造函数中的显示代码

4,成员函数作用域

#include <iostream>

using namespace std;

class InputNum
{
public:
    InputNum(char msg[])
    {
        cout << msg;
        cin >> _num;
    }
    int GetValue() const    // 该方法没有改变对象的状态,并且不允许改变数据的操作出现,如赋值,自增,自减。
    {
        return _num;
    }
    void AddInput(char msg[])
    {
        InputNum aNum(msg);
        _num = _num + aNum.GetValue();
    }

private:
    int _num;
};

const char SumString[] = "The num is ";

int main()
{
    InputNum num("Enter number ");
    num.AddInput("Add one ");
    num.AddInput("Add another ");
    cout << SumString << num.GetValue() << endl;
    return 0;
}

4.1,总结:

成员函数,析构函数, 构造函数,都会形成一个单独的本地作用域。

使用成员函数访问数据成员没有运行时开销。因为内联函数的原因。

5,抽象数据类型(文件,非内联成员函数,断言)

  • stack.h(定义接口)。
/**
 * 接口文件必须包括:
 * 类的定义,
 * 指定所有的数据成员
 * 声明成员函数
 */

const int maxStack = 16;
class IStack
{
public:
    IStack():_top(0){}
    void Push(int i);   // 将i压入堆栈
    int Pop();     // 将堆栈顶上的元素弹出,并返回
    int Size();     // 返回栈中元素
    int Top();      // 返回栈顶元素,不返回

private:
    int _arr [maxStack];
    int _top;
};

  • stack.h(函数定义)
#include "stack.h"
#include <cassert>
#include <iostream>

// 通过NDEBUG=1编译去掉断言
void IStack::Push(int i)
{
    assert(_top < maxStack);
    _arr [_top++] = i;
}

int IStack::Pop()
{
    assert(_top > 0);
    return _arr[--_top];
}

int IStack::Size()
{
    return _top;
}
int IStack::Top()
{
    assert(_top != 0);
    return _arr[_top-1];
}

  • main.cpp
#include <iostream>
#include "stack.h"

using namespace std;

int main()
{
    IStack stack;
    stack.Push(1);
    stack.Push(2);
    stack.Push(10);
    cout << stack.Size() << endl;
    cout << stack.Top() << endl;
    cout << stack.Pop() << endl;
    cout << stack.Pop() << endl;
    cout << stack.Top() << endl;
    return 0;
}

  • 输出结果
3
10
10
2
1
5.1, 断言assert(一个宏)

如果没有定义NDEBUG,编译时就将断言完全关闭,提高程序速度。

没有定义时,断言就会检查它参数的逻辑正确性,即检查是否为一个非零值,用来判断参数的真实性。

用来区别对待调试版本和发行版本。

当断言参数为假时,断言在执行时触发,打印一条指定源文件名,行号和不满足条件的信息。断言是一个调试工具。断言失败时,往往意味着程序中存在漏洞。

Assertion failed!

Program: D:\Code\Cpp\study_class\cmake-build-debug\study_class.exe
File: D:\Code\Cpp\study_class\stack.cpp, Line 14

Expression: _top > 0
5.2,关于内联函数

编译器试图将函数的实际代码插入到调用它的地方,依次来提高效率。

定义嵌入类中的成员函数由编译器自动变为内联。

将成员函数放到类外,将自动变为非内联。此时要在函数定义前面添加inline来告诉编译器变为内联。

内联只会适用于简单的情况,如函数中只有一条语句,没有复杂的程序结构(循环等),复杂结构即使使用inline和嵌入类中也会自动变为非内联。

posted @ 2020-11-13 11:25  没尾巴的刺刺鱼  阅读(108)  评论(0编辑  收藏  举报