接第三章:C++编程宝典
2.8. 内联函数(inline function)
2.8.1. 内联
c 语言中有宏函数的概念。宏函数的特点是内嵌到调用代码中去,避免了函数调用的开销。但是由于宏函数的处理发生在预处理阶段,缺失了语法检测和有可能带来的语意差错。
2.8.2. 语法
C++提供了 inline 关键字,实现了真正的内嵌。
宏函数 VS inline 函数
- #include
- #include
- using namespace std;
- #if 0
- 优点:内嵌代码,辟免压栈与出栈的开销
- 缺点: 代码替换,易使生成代码体积变大,易产生逻辑错误。
- #endif
- #define SQR(x) ((x)*(x))
- #if 0
- 优点:高度抽象,避免重复开发
- 缺点: 压栈与出栈,带来开销
- #endif
- inline int sqr(int x)
- {
- return x*x;
- }
- #endif
- int main()
- {
- int i=0;
- while(i<5)
- {
- // printf("%dn",SQR(i++));
- printf("%dn",sqr(i++));
- }
- return 0;
- }
2.8.3. 评价
优点:避免调用时的额外开销(入栈与出栈操作)
代价:由于内联函数的函数体在代码段中会出现多个“副本”,因此会增加代码段的空间。
本质:以牺牲代码段空间为代价,提高程序的运行时间的效率。
适用场景:函数体很“小”,且被“频繁”调用
2.9. 类型强转(type cast)
类型转换有 c 风格的,当然还有 c++风格的。c 风格的转换的格式很简单(TYPE)EXPRESSION,但是 c 风格的类型转换有不少的缺点,有的时候用 c 风格的转换是不合适的,因为它可以在任意类型之间转换,比如你可以把一个指向 const 对象的指针转换成指向非 const 对象的指针,把一个指向基类对象的指针转换成指向一个派生类对象的指针,这两种转换之间的差别是巨大的,但是传统的 c 语言风格的类型转换没有区分这些。还有一个缺点就是,c 风格的转换不容易查找,他由一个括号加上一个标识符组成,而这样的东西在c++程序里一大堆。所以 c++为了克服这些缺点,引进了 4 新的的类型转换操作符。
2.9.1. 静态类型转换:
语法格式:
- static_cast<目标类型> (标识符)
所谓的静态,即在编译期内即可决定其类型的转换,用的也是最多的一种。
- int a = 10;
- int b = 3;
- cout<<static_cast(a)/b<<endl;
- return 0;
2.9.3. (脱)常量类型转换:
语法格式:
- const_cast<目标类型> (标识符) //目标类类型只能是指针或引用。
- #include
- using namespace std;
- struct A
- {
- int data;
- };
- int main(void)
- {
- const A a = {200};
- // A a1 = const_cast(a);
- // a1.data = 300;
- A &a = const_cast<A&>(a);
- a2.data = 300;
- cout<<a.data<<a2.data<<endl;
- A *a3 = const_cast<A*>(&a);
- a3->data = 400;
- cout<<a.data<data<<endl;
- const int x = 3;
- int &x1 = const_cast<int&>(x);
- x1 = 300;
- cout<<x<<x1<<endl;
- int *x2 = const_cast<int*>(&x);
- *x2 = 400;
- cout<<x<<*x2<<endl;
- return 0;
- }
结论:
可以改变 const 自定义类的成员变量,但是对于内置数据类型,却表现未定义行为.
- Depending on the type of the referenced object, a write operation through the resulting pointer, reference, or pointer to data member might produce undefined behavior.
应用场景:
- #include
- using namespace std;
- void func(int & ref) //别人己经写好的程序或类库
- {
- cout<<ref<<endl;
- }
- int main(void)
- {
- const int m = 4444;
- func(const_cast<int&>(m));
- return 0;
- }
const 常变量(补充):
C++中 const 定义的变量称为常变量。变量的形式,常量的作用,用作常量,常用于
取代#define 宏常量。
- #include
- using namespace std;
- int main()
- {
- const int a = 200;
- int b = 300;
- int c = a +b
- return 0;
- }
2.9.4. 重解释类型转换:
语法格式:
- reinterpret_cast<目标类型> (标识符)
interpret 是解释的意思,reinterpret 即为重新解释,此标识符的意思即为数据的二进制
形式重新解释,但是不改变其值。
- #include
- using namespace std;
- int main()
- {
- int a[5] = {1,2,3,4,5};
- // cout<<*((int)a +1)<<endl;
- printf("%xn",*((int*)((int)a +1)));
- cout<<*(reinterpret_cast<int*>(reinterpret_cast(a)+1))<<endl;
- return 0;
- }
2.10.命名空间(namespace scope)
2.10.1. 为什么要引入namespace
命名空间为了大型项目开发,而引入的一种避免命名冲突的一种机制。比如说,在一个大型项目中,要用到多家软件开发商提供的类库。在事先没有约定的情况下,两套类库可能在存在同名的函数或是全局变量而产生冲。项目越大,用到的类库越多,开发人员越多,这种冲突就会越明显。
2.10.2. 默认NameSpace(global &&function)
global scope 是一个程序中最大的 scope。也是引起命名冲突的根源。C 语言没有从语言层面提供这种机制来解决。也算是 C 语言的硬伤了。global scope 是无名的命名空间。
- //c 语言中如何访问被局部变量覆盖的全局变量
- int val = 200;
- int main()
- {
- int *p = &val;
- int val = 100;
- printf("func val = %dn",val);
- printf("global val = %dn",*p);
- return 0;
- }
- #include
- #include
- using namespace std;
- int val = 200;
- void func()
- {
- return ;
- }
- int main()
- {
- int val = 100;
- cout<<"func val = "<<val<<endl;
- cout<<"global val = "<<::val<<endl;
- ::func(); //因为不能在函数内定义函数。所以前而的::没有意义。
- return 0;
- }
2.10.3. 语法规则
NameSpace 是对全局区域的再次划分。
声明及 namespace
- namespace NAMESPACE
- {
- 全局变量 int a;
- 数据类型 struct Stu{};
- 函数 void func();
- }
使用方法
1.直接指定 命名空间: NameSpace::a = 5;
2.使用 using+命名空间+空间元素:using NameSpace::a; a = 2000;
3.使用 using +namespace+命名空间:
- #include
- using namespace std;
- namespace MySpace
- {
- int val = 5;
- int x,y,z;
- }
- int main()
- {
- // MySpace::val = 200;
- // cout<<MySpace::val;
- // using MySpace::x;
- // using MySpace::y;
- // x = 100;
- // y = 200;
- // cout<<x<<y<<endl;
- using namespace MySpace;
- val = 1;
- x = 2;
- y = 3;
- z = 4;
- cout<<val<<x<<y<<z<<endl;
- return 0;
- }
类比 std::cout using std::cout using namespact std;
无可辟免的冲突
- #include
- using namespace std;
- namespace MySpace
- {
- int x = 1;
- int y = 2;
- }
- namespace Other {
- int x = 3;
- int y = 4;
- }
- int main()
- {
- {
- using namespace MySpace;
- cout<<x<<y<<endl;
- }
- {
- using namespace Other;
- cout<<x<<y<<endl;
- }
- {
- MySpace::x = 100;
- Other::y = 200;
- cout<<MySpace::x<<Other::y<<endl;
- }
- return 0;
- }
支持嵌套
- #include
- using namespace std;
- namespace MySpac
- {
- int x = 1;
- int y = 2;
- namespace Other {
- int m = 3;
- int n = 4;
- }
- }
- int main()
- {
- using namespace MySpace::Other;
- cout<<m<<n<<endl;
- return 0;
- }
协作开发
a.h
- #ifndef A_H
- #define A_H
- namespace XX {
- class A
- {
- public:
- A();
- ~A();
- };
- }
- #endif // A_H
a.cpp
- #include "a.h"
- using namespace XXX
- {
- A::A()
- {
- }
- A::~A()
- {
- }
- }
b.h
- #ifndef B_H
- #define B_H
- namespace XX
- {
- class B
- {
- public:
- B();
- ~B();
- };
- }
- #endif // B_H
b.cpp
- #include "b.h"
- namespace XX {
- B::B()
- {
- }
- B::~B()
- {
- }
- }
main.cpp
- #include
- #include "a.h"
- #include "b.h"
- using namespace std;
- using namespace XX;
- int main()
- {
- A a;
- B b;
- return 0;
- }
2.11.系统 string 类
除了使用字符数组来处理字符串以外,c++引入了字符串类型。可以定义字符串变量。
2.11.1. 定义及初始化
- int main()
- {
- string str;
- str = "china";
- string str2 = " is great ";
- string str3 = str2;
- cout<<str<<str2<<endl<<str3<<endl;
- return 0;
- }
2.11.2. 类型大小
- cout<<"sizeof(string) = "<<sizeof(string)<<endl;
- cout<<"sizeof(str) = "<<sizeof(str)<<endl;
2.11.3. 运算
赋值
- string str3 = str2;
加法
- string combine = str + str2;
- cout<<combine<<endl;
关系
- string s1 = "abcdeg";
- string s2 = "12345";
- if(s1>s2)
- cout<<"s1>s2"<<endl;
- else
- cout<<"s1<s2"<<endl;
- string s3 = s1-s2;
- cout<<s3<<endl;
2.11.4. string 类型数组
- string sArray[10] = {
- "0",
- "1",
- "22",
- "333",
- "4444",
- "55555",
- "666666",
- "7777777",
- "88888888",
- "999999999",
- };
- for(int i=0; i<10; i++)
- {
- cout<<sArray[i]<<endl;
- }
string 数组是高效的,如果用二维数组来存入字符串数组的话,则容易浪费空间,此时列数是由最长的字符串决定。如果用二级指针申请堆空间,依据大小申请相应的空间,虽然解决了内存浪费的问题,但是操作麻烦。用string 数组存储,字符串数组的话,效率即高又灵活。
2.12.C++之父给 C 程序员的建议
1、在 C++中几乎不需要用宏,用 const 或 enum 定义明显的常量,用 inline 避免函数调用的额外开销,用模板去刻画一族函数或类型,用 namespace 去避免命名冲突。
2、不要在你需要变量之前去声明,以保证你能立即对它进行初始化。
3、不要用 malloc,new 运算会做的更好
4、避免使用 void*、指针算术、联合和强制,大多数情况下,强制都是设计错误的指示器。
5、尽量少用数组和 C 风格的字符串,标准库中的 string 和 vector 可以简化程序
6、更加重要的是,试着将程序考虑为一组由类和对象表示的相互作用的概念,而不是一堆数据结构和一些可以拨弄的二进制。
2.13.练习
2.13.1. 练习1用 cout 的格式控制,打一个时钟。
2.13.2. 练习2读字符串 char buf[100] = “xxxx:yyyy:zzzz:aaaa:bbb”.按:进行分解到,string 数组
C++封装在下一章继续讲解
[…] […]