C++笔记

C++笔记

双冒号的作用

1.作用域符号::的前面一般是类名称,后面一般是该类的成员名称,C++为例避免不同的类有名称相同的成员而采用作用域的方式进行区分

如:A,B表示两个类,在A,B中都有成员member。那么
A::member就表示类A中的成员member
B::member就表示类B中的成员member

注:在什么场合下使用?

2.全局作用域符号:当全局变量在局部函数中与其中某个变量重名,那么就可以用::来区分如:

~ C++
char zhou; //全局变量
void sleep()

char zhou; //局部变量
char(局部变量) = char(局部变量) *char(局部变量) ;
::char(全局变量) =::char(全局变量) *char(局部变量);

~

3.::是C++里的“作用域分解运算符”。比如声明了一个类A,类A里声明了一个成员函数voidf(),但没有在类的声明里给出f的定义,那么在类外定义f时,就要写成voidA::f(),表示这个f()函数是类A的成员函数。例如:

~c++
01 class CA {
02 public:
03 int ca_var;
04 int add(int a, int b);
05 int add(int a);
06 };
07
08 //那么在实现这个函数时,必须这样书写:
09 int CA::add(int a, int b)
10 {
11 return a + b;
12 }
13
14 //另外,双冒号也常常用于在类变量内部作为当前类实例的元素进行表示,比如:
15 int CA::add(int a)
16 {
17 return a + ::ca_var;
18 }
19
20 //表示当前类实例中的变量ca_var。
~

c++编译原理

参看:http://www.cnblogs.com/Lynn-Zhang/p/5377024.html

C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件操作系统的启动代码和用到的库文件进行组织,形成最终生成可执行代码的过程。过程图解如下:

编译原理图

从图上可以看到,整个代码的编译过程分为编译和链接两个过程,编译对应图中的大括号括起的部分,其余则为链接过程。

一、编译过程

编译过程又可以分成两个阶段:编译汇编

编译

编译是读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,源文件的编译过程包含两个主要阶段:

  • 编译预处理

读取c源程序,对其中的伪指令(以# 开头的指令)和特殊符号进行处理。

伪指令主要包括以下四个方面:

1) 宏定义指令,如# define Name TokenString,# undef等。

对于前一个伪指令,预编译所要做的是将程序中的所有Name用TokenString替换,但作为字符串常量的 Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。

2) 条件编译指令,如# ifdef,# ifndef,# else,# elif,# endif等。

这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。

3) 头文件包含指令,如# include “FileName” 或者# include < FileName> 等。

在头文件中一般用伪指令# define定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。

采用头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用。因为在需要用到这些定义的C源程序中,只需加上一条# include语句即可,而不必再在此文件中将这些定义重复一遍。预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。

包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在/ usr/ include目录下。在程序中# include它们要使用尖括号(< >)。另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在# include中要用双引号(”“)。

4) 特殊符号,预编译程序可以识别一些特殊的符号。

例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。

预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。下一步,此输出文件将作为编译程序的输入而被翻译成为机器指令。

  • 编译、优化阶段

经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C语言的关键字,如main, if , else , for , while , { , } , + , - , * , \ 等等。

编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。

优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的。

对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。

后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是一个重要的研究课题。

经过优化得到的汇编代码必须经过汇编程序的汇编转换成相应的机器指令,方可能被机器执行。

汇编

汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。

目标文件由段组成。通常一个目标文件中至少有两个段:

1) 代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。

2) 数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

UNIX环境下主要有三种类型的目标文件:

1) 可重定位文件

其中包含有适合于其它目标文件链接来创建一个可执行的或者共享的目标文件的代码和数据。

2) 共享的目标文件

这种文件存放了适合于在两种上下文里链接的代码和数据。

第一种是链接程序可把它与其它可重定位文件及共享的目标文件一起处理来创建另一个目标文件;

第二种是动态链接程序将它与另一个可执行文件及其它的共享目标文件结合到一起,创建一个进程映象。

3) 可执行文件

它包含了一个可以被操作系统创建一个进程来执行之的文件。

汇编程序生成的实际上是第一种类型的目标文件。对于后两种还需要其他的一些处理方能得到,这个就是链接程序的工作了。

二、链接过程

由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。

例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。

链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。

根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:

静态链接库不同于动态链接库(.dll),在静态库情况下,函数和数据被编译进一个二进制文件(通常扩展名为.LIB),Visual C++的编译器在链接过程中将从静态库中恢复这些函数和数据并把他们和应用程序中的其他模块组合在一起生成可执行文件。这个过程称为”静态链接”,此时因为应用程序所需的全部内容都是从库中复制了出来,所以静态库本身并不需要与可执行文件一起发行。备注:静态库在程序的发行时是不需要发布库的, 因为静态链接就是将程序所需的所有程序直接拷贝到程序中来。

在应用中,有一些公共代码是需要反复使用,就把这些代码编译为“库”文件;在连接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件中。这种库称为静态库,其特点是可执行文件中包含了库代码的一份完整拷贝;缺点就是被多次使用就会有多份冗余拷贝。为了克服这个缺点可以采用动态连接库。这个时候连接器仅仅是在可执行文件中打上标志,说明需要使用哪些动态连接库;当运行程序时,加载器根据这些标志把所需的动态连接库加载到内存。

备注:动态库,肯定需要将程序的动态库一起发布,这样就可以减少程序的大小。动态库(DLL),静态库(LIB)

三、GCC的编译链接

我们在linux使用的gcc编译器便是把以上的几个过程进行捆绑,使用户只使用一次命令就把编译工作完成,这的确方便了编译工作,但对于初学者了解编译过程就很不利了,下图便是gcc代理的编译过程:

gcc编译原理

从上图可以看到:

1) 预编译

将.c 文件转化成 .i文件

使用的gcc命令是:gcc –E

对应于预处理命令cpp

2) 编译

将.c/.h文件转换成.s文件

使用的gcc命令是:gcc –S

对应于编译命令 cc –S

3) 汇编

将.s 文件转化成 .o文件

使用的gcc 命令是:gcc –c

对应于汇编命令是 as

4) 链接

将.o文件转化成可执行程序

使用的gcc 命令是: gcc

对应于链接命令是 ld

总结起来编译过程就上面的四个过程:预编译处理(.c) --> 编译、优化程序(.s、.asm)--> 汇编程序(.obj、.o、.a、.ko) --> 链接程序(.exe、.elf、.axf等)。

四、总结

C语言编译的整个过程是非常复杂的,里面涉及到的编译器知识、硬件知识、工具链知识都是非常多的,深入了解整个编译过程对工程师理解应用程序的编写是有很大帮助的,希望大家可以多了解一些,在遇到问题时多思考、多实践。

一般情况下,我们只需要知道分成编译和链接两个阶段,编译阶段将源程序(*.c) 转换成为目标代码(一般是obj文件,至于具体过程就是上面说的那些阶段),链接阶段是把源程序转换成的目标代码(obj文件)与你程序里面调用的库函数对应的代码连接起来形成对应的可执行文件(exe文件)就可以了,其他的都需要在实践中多多体会才能有更深的理解。

c++在linux中的编译

单个文件编译

下面是一个保存在文件 helloworld.cpp 中一个简单的 C++ 程序的代码: 单个源文件生成可执行程序

~~~c++
/* helloworld.cpp */

include

int main(int argc,char *argv[])
{
std::cout << “hello, world” << std::endl;
return(0);
}
~~~

程序使用定义在头文件 iostream 中的 cout,向标准输出写入一个简单的字符串。该代码可用以下命令编译为可执行文件:

$  g++ helloworld.cpp

编译器 g++ 通过检查命令行中指定的文件的后缀名可识别其为 C++ 源代码文件。
编译器默认的动作:编译源代码文件生成对象文件(object file),链接对象文件和 libstd c++ 库中的函数得到可执行程序。然后删除对象文件。由于命令行中未指定可执行程序的文件名,编译器采用默认的 a.out。程序可以这样来运行:

$ ./a.out
hello, world

更普遍的做法是通过 -o 选项指定可执行程序的文件名。下面的命令将产生名为 helloworld 的可执行文件:

$ g++ helloworld.cpp -o helloworld#注意-o是生成可执行文件!

在命令行中输入程序名可使之运行:

$ ./helloworld
hello, world

程序 g++ 是将 gcc 默认语言设为 C++ 的一个特殊的版本,链接时它自动使用 C++ 标准库而不用 C 标准库。通过遵循源码的命名规范并指定对应库的名字,用 gcc 来编译链接 C++ 程序是可行的,如下例所示:

$ gcc helloworld.cpp -lstdc++ -o helloworld

多个源文件生成可执行程序
如果多于一个的源码文件在 g++ 命令中指定,它们都将被编译并被链接成一个单一的可执行文件。下面是一个名为 speak.h的头文件;它包含一个仅含有一个函数的类的定义:

/* speak.h */
include <iostream>
class Speak{
    public:
        void sayHello(const char *);
};

下面列出的是文件 speak.cpp的内容:包含 sayHello()函数的函数体:

~~~ c++
/* speak.cpp */

include “speak.h”

void Speak::sayHello(const char *str)
{
std::cout << “Hello ” << str << “\n”;
}
~~~

文件hellospeak.cpp内是一个使用 Speak 类的程序:

~~~c++
/* hellospeak.cpp */

include “speak.h”

int main(int argc,char *argv[])
{
Speak speak;
speak.sayHello(“world”);
return(0);
}
~~~

下面这条命令将上述两个源码文件编译链接成一个单一的可执行程序:

~ shell
$ g++ hellospeak.cpp speak.cpp -o hellospeak
~

PS:这里说一下为什么在命令中没有提到“speak.h“该文件(原因是:在“speak.cpp“中包含有”#include”speak.h”“这句代码,它的意思是搜索系统头文件目录之前将先在当前目录中搜索文件“speak.h“。而”speak.h“正在该目录中,不用再在命令中指定了)。

源文件生成对象文件
选项 -c 用来告诉编译器编译源代码但不要执行链接,输出结果为对象文件。文件默认名与源码文件名相同,只是将其后缀变为.o。例如,下面的命令将编译源码文件 hellospeak.cpp并生成对象文件 hellospeak.o:

$ g++ -c hellospeak.cpp
命令 g++ 也能识别 .o 文件并将其作为输入文件传递给链接器。下列命令将编译源码文件为对象文件并将其链接成单一的可执行程序:

g++chellospeak.cpp g++ -c speak.cpp
$ g++ hellospeak.o speak.o -o hellospeak
选项 -o 不仅仅能用来命名可执行文件。它也用来命名编译器输出的其他文件。例如:除了中间的对象文件有不同的名字外,下列命令生将生成和上面完全相同的可执行文件:

g++chellospeak.cppohspk1.o g++ -c speak.cpp -o hspk2.o
$ g++ hspk1.o hspk2.o -o hellospeak
编译预处理
选项 -E使 g++ 将源代码用编译预处理器处理后不再执行其他动作。下面的命令预处理源码文件 helloworld.cpp 并将结果显示在标准输出中:

$ g++ -E helloworld.cpp
本文前面所列出的 helloworld.cpp 的源代码,仅仅有六行,而且该程序除了显示一行文字外什么都不做,但是,预处理后的版本将超过 1200 行。这主要是因为头文件 iostream 被包含进来,而且它又包含了其他的头文件,除此之外,还有若干个处理输入和输出的类的定义。
预处理过的文件的 GCC 后缀为.ii,它可以通过 -o 选项来生成,例如:

$ gcc -E helloworld.cpp -o helloworld.ii
生成汇编代码
选项 -S指示编译器将程序编译成汇编语言,输出汇编语言代码而后结束。下面的命令将由 C++ 源码文件生成汇编语言文件 helloworld.s:

$ g++ -S helloworld.cpp
生成的汇编语言依赖于编译器的目标平台。

创建静态库
静态库是编译器生成的一系列对象文件的集合。链接一个程序时用库中的对象文件还是目录中的对象文件都是一样的。库中的成员包括普通函数,类定义,类的对象实例等等。静态库的另一个名字叫归档文件(archive),管理这种归档文件的工具叫 ar 。
在下面的例子中,我们先创建两个对象模块,然后用其生成静态库。
头文件 say.h包含函数 sayHello()的原型和类 Say 的定义:

/* say.h */

include

void sayhello(void);
class Say {
private:
char *string;
public:
Say(char *str)
{
string = str;
}
void sayThis(const char *str)
{
std::cout << str << ” from a static library\n”;
}
void sayString(void);
};
下面是文件say.cpp是我们要加入到静态库中的两个对象文件之一的源码。它包含 Say 类中 sayString()函数的定义体;类 Say 的一个实例 librarysay的声明也包含在内:

/* say.cpp */

include “say.h”

void Say::sayString()
{
std::cout << string << “\n”;
}
Say librarysay(“Library instance of Say”);
源码文件 syshello.cpp 是我们要加入到静态库中的第二个对象文件的源码。它包含函数 sayhello() 的定义:

/* sayhello.cpp */

include “say.h”

void sayhello()
{
std::cout << “hello from a static library\n”;
}
下面的命令序列将源码文件编译成对象文件,命令 ar 将其存进库中:

g++csayhello.cpp g++ -c say.cpp
$ ar -r libsay.a sayhello.o say.o
程序 ar 配合参数 -r 创建一个新库 libsay.a 并将命令行中列出的对象文件插入。采用这种方法,如果库不存在的话,参数 -r 将创建一个新的库,而如果库存在的话,将用新的模块替换原来的模块。
下面是主程序 saymain.cpp,它调用库 libsay.a 中的代码:

/* saymain.cpp */

include “say.h”

int main(int argc,char *argv[])
{
extern Say librarysay;
Say localsay = Say(“Local instance of Say”);
sayhello();
librarysay.sayThis(“howdy”);
librarysay.sayString();
localsay.sayString();
return(0);
}
该程序可以下面的命令来编译和链接:

$ g++ saymain.cpp libsay.a -o saymain
程序运行时,产生以下输出:
hello from a static library
howdy from a static library
Library instance of SayLocal instance of Say

以上参考:http://www.cnblogs.com/ucas/p/5778664.html

c++ make和makefile

c++文件修改之后是否所有的文件都需要重新编译?

c++ enum

c++ 常引用

命名空间

智能指针

智能指针shared_ptr的用法:http://www.cnblogs.com/jiayayao/archive/2016/12/03/6128877.html

为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer)。

智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈上的变量自动被销毁,智能指针内部保存的内存也就被释放掉了(除非将智能指针保存起来)。问题:智能指针在栈区,智能指针指向

  C++11提供了三种智能指针:std::shared_ptr, std::unique_ptr, std::weak_ptr,使用时需添加头文件。

  shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,删除所指向的堆内存。shared_ptr内部的引用计数是安全的,但是对象的读取需要加锁。

参看:

http://www.cnblogs.com/ForFreeDom/archive/2012/03/22/2412055.html

http://blog.csdn.net/u012632851/article/details/52148814

总结如下;

1.为什么用枚举?

​ 写程序时,我们常常需要为某个对象关联一组可选alternative属性.例如,学生的成绩分A,B,C,D等,天气分sunny, cloudy, rainy等等。更常见的,打开一个文件可能有三种状态:input, output和append. 典型做法是,对应定义3个常数,即:

const int input = 1; 
const int output = 2; 
const int append = 3;

然后,调用以下函数:

~c++
bool open_file(string file_name, int open_mode);
~

比如,

~ c++
open_file("Phenix_and_the_Crane", append);
~

这种做法比较简单,但存在许多缺点:无法限制传递给open_file函数的第2个参数的取值范围,只要传递int类型的值都是合法的。(当然,这样的情况下的应对措施就是在open_file函数内部判断第二个参数的取值,只有在1,2,3范围内才处理。)
​ 使用枚举能在一定程度上减轻这种尴尬(注1),它不但能实现类似于之前定义三个常量的功能,还能够将这三个值组合起来成为独一无二的组。例如:
​ enum open_modes {input = 1, output, append};
​ 以上定义了open_modes为枚举类型enumeration type。每一个命名了的枚举都是唯一的类型,是一个类型标示器type specifier。例如,我们可以重新写一个open_file函数:
​ bool open_file(string file_name, open_modes om);
​ 在open_modes枚举中,input, output, append称为枚举子enumerator, 它们限定了open_modes定义的对象的取值范围。这个时候,调用open_file函数和之前的方法还是一模一样:
​ open_file(“Phenix_and_the_Crane”, append);
​ 但是,如果传递给open_file的第二个参数不是open_modes枚举类型值的话(注1),那么编译器就会识别出错误;就算该参数取值等价于input, output, append中的某个,
也一样会出错哦!例如:
​ open_file(“Phenix_and_the_Crane”, 1);

eclipse

参看:

1.http://blog.csdn.net/zhu2695/article/details/51511507

2.http://blog.csdn.net/mercury912/article/details/52267145

eclipse的使用:

http://blog.csdn.net/challenge_c_plusplus/article/details/7364354

debug:

https://www.youtube.com/watch?v=azInZkPP56Q

自动补全功能:

http://blog.csdn.net/jiangxinyu/article/details/7804884

进入到windows→preferences→editor→content assist→全都勾起来!

eclipse无法使用智能指针

http://www.cnblogs.com/imjch/p/3902937.html

Symbol.27shared_ptr.27_could_not_be_resolved”>http://wiki.opencog.org/w/Using_Eclipse_CDT#Fix:Symbol.27shared_ptr.27_could_not_be_resolved

caffe调试

caffe调试工具:http://www.jianshu.com/p/6a9ef72dac09

Armadillo的介绍和配置

http://blog.csdn.net/fiona_ffflll/article/details/40661535

Armadillo教程:

http://blog.csdn.net/jnulzl/article/details/46808515

## MiniNet的github地址

https://github.com/hgaolbb/MiniNet/blob/master/cpp/include/layer.hpp

MiniNet架构分析

Blob

提供三种构造函数:输入n,c,h,w和默认初始化type,输入shape和默认初始化type,输入shape和随机初始化type

操作符重载:

1.blob[i],获取第i个元素,从0开始

2.blob=num,blob中的元素都设置为num

3.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值