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:CodeCppstudy_classcmake-build-debugstudy_class.exe
File: D:CodeCppstudy_classstack.cpp, Line 14
Expression: _top > 0
5.2,关于内联函数
编译器试图将函数的实际代码插入到调用它的地方,依次来提高效率。
定义嵌入类中的成员函数由编译器自动变为内联。
将成员函数放到类外,将自动变为非内联。此时要在函数定义前面添加inline来告诉编译器变为内联。
内联只会适用于简单的情况,如函数中只有一条语句,没有复杂的程序结构(循环等),复杂结构即使使用inline和嵌入类中也会自动变为非内联。