0%

C++进阶学习

之前的C++学习都是浅尝辄止,这一次直接购入《C++ Primer Plus》系统深入的学习一些C++的知识,尤其C++擅长的面向对象部分。

引用表在文章末尾

union

union即为联合,它是一种特殊的类。通过关键字union进行定义,一个union可以有多个数据成员。
在任意时刻,联合中只能有一个数据成员可以有值。当给联合中某个成员赋值之后,该联合中的其它成员就变成未定义状态了。
在C/C++程序的编写中,当多个基本数据类型或复合数据结构要占用同一片内存时,我们要使用联合体;当多种类型,多个对象,多个事物只取其一时(我们姑且通俗地称其为“n 选1”),我们也可以使用联合体来发挥其长处。
union主要是共享内存,分配内存以其最大的结构或对象为大小,即sizeof最大的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
union myun 
{
struct { int x; int y; int z; }u;
int k;
}a;
int main()
{
a.u.x =4;
a.u.y =5;
a.u.z =6;
a.k = 0;
printf("%d %d %d\n",a.u.x,a.u.y,a.u.z);
return 0;
}
1234567891011121314

由于union类型是共享内存,以size最大的结构作为自己的大小,这样的话,myun这个结构就包含u这个结构体,而大小也等于u这个结构体 的大小,在内存中的排列为声明的顺序x,y,z从低到高,然后赋值的时候,在内存中,就是x的位置放置4,y的位置放置5,z的位置放置6,现在对k赋 值,对k的赋值因为是union,要共享内存,所以从union的首地址开始放置,首地址开始的位置其实是x的位置,这样原来内存中x的位置就被k所赋的值代替了,就变为0了,这个时候要进行打印,就直接看内存里就行了,x的位置也就是k的位置是0,而y,z的位置的值没有改变,所以应该是0,5,6。

EasyX的ExMessage的定义就涉及到了union的使用,有兴趣可以看看我写的EasyX的博客。

多文件编译

稍大点的项目一般就不会使用单文件编程。毕竟多文件的好处实在是多,例如模块开发、分工协作、代码复用、结构清晰、方便模块更新。

那么怎么使用C++进行多文件编程呢?最先要搞懂的就是C++程序的文件类型、

文件类型

C++程序常用的文件有两大类

头文件

头文件常常以.h结尾,通常被用于放置各种声明,用于被主程序文件包含

头文件的存在是为了联系多个源文件,是源文件之间的接口。C++与C一样,要求先声明后使用,可是编译的时候是单文件编译的。C++编译过程中C++把每个文件单独编译出来,再通过链接把编译出来的多个文件组成一个可执行程序。编译的时候只检查函数声明,只要该文件能在之前声明过函数就能编译成功。函数定义是在链接阶段检查的,而链接是多文件共同参与的。

但是,每次使用其它文件的函数前都要自己先声明显然很繁琐也容易出错。所以,我们把声明类语句放到一类文件里,称之“头文件”,如果你需要使用到某个函数,就把它所在的头文件包含进来,头文件的内容会在编译前被粘贴到源文件中,这样在编译的时候就能正常通过了。

头文件的内容一般都会使用条件编译预处理语句包住,防止因为依赖关系多次被包含。

1
2
3
4
5
//header.h
#ifndef HEADER_H
#define HEADER_H
void fun();
#endif

既然知道头文件的作用,那哪些东西应该放在头文件?哪些不能放在头文件?很好理解,如果这部分需要复制给每个相关的cpp,就把它放在头文件,如果被多个cpp复制之后,可能导致它们在链接过程出错,就不要放在头文件。 一 一来看:

函数声明:显然应该放在头文件中,前面很清楚。

类定义、结构定义:用函数定义的逻辑想,似乎不能放在头文件中。但它应该放在头文件。第一,每个cpp文件应该有一个定义,在编译的时候编译器才知道怎么为对象分配空间。其次,类型定义不会在内存上分配空间。

模板函数:编译器必须在编译的时候根据函数模板实例化对应的函数,所以应该放在头文件。

内联函数:编译期间被插到调用位置,所以也要放在头文件。

函数定义:不要!C++规定一个程序同签名的函数只能有一个定义。如果你把函数定义放在头文件件,并且同一个程序的多个cpp文件包含了该头文件,这样,在链接的时候会发现多个定义版本,链接报错。

变量定义:不要!与上面类似,被多个文件包含的时候会出现多次定义同一个变量,链接错误。但是,static变量和extern变量可以,以及宏定义的常量,因为这些在多个文件出现并不会出错。

CPP文件

.cpp文件包含模块文件和主程序文件。

主程序文件,顾名思义,就是包含main()函数的文件,是作为程序入口的存在,由它来调用模块文件实现的函数。

模块文件一般存放的是一些函数定义,因此也称为功能模块。

多文件的编译可以一键Build也可以先变异成.o再链接(这种方法会便于模块化更新)

extern变量声明

extern很早就遇到过,但实际用的并不多。

类似于函数的声明和定义,变量也同样可以声明和定义。

引用cnblogs@小人物702的一段话

很多人看了可能糊涂,这里稍微说一下,其实就是变量定义变量声明的区别,变量定义使用“数据类型+变量名称”的形式,编译器需要给他分配内存单元的;而变量声明使用“extern 变量类型+变量名称”的形式,是告诉编译器我这个变量将在其他外部c文件中定义,我这里只是在外部用它。编译器就不给他分配内存空间,而等到真正遇到变量定义的时候再给他分配内存空间。

  由于很多人从开始学C语言就一直把定义变量声明变量,一开始就叫错了,所以导致现在分不清定义声明的区别。要是还理解不了就想想函数的定义声明,函数定义是编写函数功能实体,编译器要编译这个函数并且要分配内存空间,而函数声明并不生成函数功能实体,只是告诉编译器这是个函数,这个函数在后面将会定义实体,我这里只是提前用,编译器就会接着继续往下编译,如果子函数写在main函数之后,那么声明是必须的,如果不声明函数编译器都不知道这是个函数,编译就会报错。

数据结构

链队列

链式队列,队列的一种形式。

队列,在《大话数据结构》中是这样定义的:

队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表。

其他

inline内联函数

例如

1
2
3
4
5
inline void init(int n)
{
for (int i = 1; i <= n; ++i)
fa[i] = i;
}

优点:

1)inline定义的内联函数,函数代码被放入符号表中,在使用时进行替换(像宏一样展开),效率很高。

2)类的内联函数也是函数。编绎器在调用一个内联函数,首先会检查参数问题,保证调用正确,像对待真正函数一样,消除了隐患及局限性。

3)inline可以作为类的成员函数,刀可以使用所在类的保护成员及私有成员。

缺点:

内联函数以复制为代价,活动产函数开销

1)如果函数的代码较长,使用内联将消耗过多内存

2)如果函数体内有循环,那么执行函数代码时间比调用开销大。

.和->的差异

c++中 . 和 -> 主要是用法上的不同。

1、A.B则A为对象或者结构体;

2、A->B则A为指针,->是成员提取,A->B是提取A中的成员B,A只能是指向类、结构、联合的指针;

例如:

1
2
3
4
5
6
7
8
9
class student

{

public:

string name[20];

}

第一种情况,采用指针访问 student xy,则访问时需要写成 xy.name=”hhhhh”;等价于xy->name=”hhhhh”。

第二种情况,采用普通成员访问 student xy,则访问时需要写成 xy.name=”hhhhh”。

指针传递和引用传递

指针传递在定义形参的时候定义为指针;使用引用传递的话需要在定义形参的时候在变量名前面加一个‘&’。

for(auto i : v)

C++11新特性

范围for,相当于java的for each。v是一个可遍历的容器或流,比如vector类型,i就用来在遍历过程中获得容器里的每一个元素。

1
2
3
4
5
vector<int> v={1,2,3,4};

for(auto i:v)

cout<<i;

结果就是1234

一些常用但是我不会的小细节

string初始化

方式一 :

最简单直接, 直接赋值

1
string str1 = "test01" ;

方式二:

1
string( size_type length, char ch );

以length为长度的ch的拷贝(即length个ch)

1
string str2( 5, 'c' );  //  str2 'ccccc'

方式三 :

1
string( const char *str );
1
string str3( "Now is the time..." );

方式四:

1
string( string &str, size_type index, size_type length );

以index为索引开始的子串,长度为length, 或者 以从start到end的元素为初值.

1
string str4( str3, 11, 4 );  //将str3

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;


int main() {
string str1 = "test01" ;
string str2( 5, 'c' ); // str2 'ccccc'
string str3( "Now is the time..." );
string str4( str3, 11, 4 );

cout << str1 << endl;
cout << str2 << endl;
cout << str3 << endl;
cout << str4 << endl;

return 0;

}

运行结果

引用及致谢(链接指向原文)

union部分:CSDN@W Y

extern部分:cnblogs@小人物702

多文件部分:CSDN@czpcalm

String初始化部分:CSDN@zhangbw~

inline部分:CSDN@tsinfeng

-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道