Java程序员的C++回归路(一)
前言:工作后吃饭的语言是java,同时写一些python和js,在学习机器学习的时候发现有必要再熟悉一下c++,同时工作也有c++的使用需求。于是开始对照c++ primer自学,希望能够对同样是其他语言的学习者,在学习c++的时候提供一些帮助。
第1章: 起始
First program
主流编译器:GNU 编译器和微软的编译器,运行微软编译器的命令是:cl
Input/Output
Using namespace used to avoid inadvertent collsions between the same names.
Std::cin返回 std::cin对象
注释
C++中有两种类型的注释:单行注释://和多行注释:/* */
如果有程序如下:
#include <iostream>
int main(){
int sum = 0, value = 0;
while(std::cin >> value){
sum += value;
}
std::cout<< "Sum is: "<< sum << std::endl;// std::endl flush buffer
}
上面这段程序会一直读取输入的内容直到输入的结尾,在不同的操作系统中,标识输入的结尾是不同的,在类unix操作系统中,使用ctrl + D来表示输入结尾,在windows系统中,使用ctrl+Z标识。
关于编译器
在类unix系统(含Mac)使用到的编译器和编译指令如下:
cc:在Mac上,cc作为clang的软链接。
clang:相比于gcc,clang有着编译速度快,编译产出小,编译提示友好等有点。而且使用c++编写,基于LLVM的C/C++/Objective C/Objective C++ 编译器。
gcc:GNU的c编译器,后面发展为可以编译很多的编程语言。
g++:c++的编译器。
msvc:windows上使用的c/c++编译器
关于这些区别的一篇博文:https://www.cnblogs.com/qoakzmxncb/archive/2013/04/18/3029105.html
C++中的类
使用一个库的时候,需要包含关联的header文件。标准库的headers一般是不包含后缀名的,编译器一般不关心文件的后缀,但是IDE有时会。
clog的使用,默认地,写到clog中的数据会被buffer缓冲,一般用于报告程序运行过程中的信息。
第2章:基础
C++的原始内建类型
C++中包含了一些基本的数学类型和void类型,作为原始内建类型。
Type | Meaning | Minimum Size |
---|---|---|
bool | boolean | NA |
char | character | 8bits |
wchar_t | wide character | 16bits |
char16_t | Unicode character | 16bits |
char32_t | Unicode character | 32bits |
short | short integer | 16bits |
int | integer | 16bits(所有操作系统?) |
long | long integer | 32bits |
long long | long integer | 64bits |
float | Single-precision floating-point | 6 significant digits |
double | Double-precision | 10 significant digits |
long double | Extended-precision | 10 significant digits |
需要注意的是,在上述表格中的数据类型所占用的内存大小根据平台的不同而不同。
确定内存地址的数据需要数据类型和读取内存地址的二进制数据,不同的数据类型决定了占用多少比特以及如何解析这些内存数据。
上述的int、long、long long类型都是有符号数,对应的无符号数前面加上unsigned。
数据类型的后缀中是U时,字面量是unsigned类型,类型可以使unsigned int,unsigned long或unsigned long long 类型;
如果后缀是L,字面量类型时long;
如果后缀是LL,字面量类型时long long 或者unsigned long long
如果后缀是UL或ULL,字面量类型时unsigned long或unsigned long long
前缀类型表:
Meaning | Prefix | Type |
---|---|---|
u | Unicode 16 character | Char16_t |
U | Unicode 32 character | char32_t |
L | wide character | wchar_t |
u8 | utf-8 | char |
初始化和定义
C++提供了多种初始化的方式:
int unit = 0;
int unit = {0};
int unit{0};//列表初始化
int unit(0);
编译器不允许使用列表初始化时类型信息丢失:
long double ld = 3.1415926
Declaration & Definition
声明:声明一个名称让程序知道
定义:定义除了声明名称和类型,同时申请内存和提供默认值
为了得到一个变量的声明而不是定义,我们使用extern关键字,而且不显示的初始化变量。
extern int i; // 声明变量
int j; // 定义变量
一个变量可以被声明多次,但是只能被定义一次。
作用域
全局作用域:定义在函数体之外的变量
块作用域:{}内的作用域
复合类型
C++中有多种复合类型,这里记录指针和引用。
引用为对象起了另外一个名字,通过&d来定义引用类型,其中d是变量名
int a = 1;
int &d = a; // 声明引用,d是a的另外一个名字
int &d2; // 报错,引用必须初始化
引用非对象,相反的,它是为对象取了一个别名
为引用赋值,实际是赋值给引用的对象;获取引用的值,获取的是对象的值;将引用的值作为初始值,实际上是将引用对象的值作为初始值。
因为引用本身不是对象,所以不能定义引用的引用。
无法将引用绑定到另外一个对象,因此引用必须初始化。
可以给引用赋值(等于给别名赋值),如下代码所示:
int i = 0;
int &ri = i;
ri = 10; // legal,这里等于是给i进行赋值
指针
指针时一个指向其他类型的复合类型,值是指向对象的地址。
指针本身是一个对象。
指针值
指针的值可以指向一个对象
可以指向刚刚读取完的对象的值(类似于Iterator执行的对象)
可以是个空指针,即没有绑定任务对象
invalid指针,除了上述三种指针的值都是不合法的。
指针的指针
可以使用**p获取指针的指针所在对象的值。
void*
void*是一个特别的指针类型,可以保存任意对象的地址
在理解类似于 *&p这种类型的时候,将修饰符从右往左读去理解。
const修饰符
const修饰的类型有普通类型的大部分操作,如将const int类型转为bool类型。
int i = 0;
const int ci = i;
int j = ci;
上述代码中关于const的操作:给ci赋值的时候不会考虑ci是常量类型,因为不会改变常量的值,同样的,将ci赋值给j的时候也是如此。
默认情况下,const对象仅在文件中有效。
如果需要在不同文件间共享const变量的值,则使用extern用于声明常量并非本文件独有。
常量引用
常量的引用类型需要使用常量引用,如下所示:
const int ci = 1;
const int &ri = ci;
int &r2 = ri; // error:non const reference to a const object
const pointer
指针本身是const,例如:
int num = 0;
int *const curNum = # // curNum will always point to num
const double pi = 3.14;
const double *const pip = π // pip is a const pointer to a const object
Top-level const
使用top-level const来标识指针本身是一个常量;如果指针指向一个const对象,那么我们说这个const是low-level const。
constexpr
常量表达式:当做const表达式时可以使用constexpr
类型处理
typedef
alias
格式为:using a = A;
auto
自动判断类型
decltype
自动判断类型,但是不计算变量的值
decltype()中的解引用操作返回的结果是引用,而不是原始类型
在decltype中添加一对以上的括号,将返回引用类型
struct
//定义struct的两种格式
struct{
};
struct {...} a,b,*c;
struct中定义的成员变量会在对象定义的时候被默认初始化。
定义头文件
一般只定义一次的内容放在头文件中,如类、const、constexpr变量等。
头文件一旦改变,相关的源文件需要重新编译来获取新的变量声明。
确保头文件多次包含仍能被正确处理的机制是预处理器(preprocessor)
预处理器看到#include会将内容替换掉#include
头文件保护符:使用以下代码来避免重复包含的发生:
#ifndef CPP_TEST_SALES_DATA_H
#define CPP_TEST_SALES_DATA_H
struct sales_data{
};
#endif //CPP_TEST_SALES_DATA_H
与编译器无视关于作用域的规则
一般的做法是基于类的名字来构建保护符的名字
字:在指定机器上进行整数运算的自然单位
字符串、vector
using
格式为:using namespace::name
头文件不应该包含using描述,否则可能有名字冲突。
宏
C++中将一个标识符定义为一个字符串,源程序中的标识符用字符串代替。如:
#define ADD (x,y) x+y
result=ADD(2, 3); // 使用 x+y替换
string
初始化string的方式
string s(4, 'c') // 初始化为"cccc",直接初始化
string s2("hello") // 直接初始化
string s3 = "hello" //拷贝初始化
其他初始化方式省略
string的操作
getline(is, s) //从is中返回一行赋值给s,返回is
其他操作省略
string::size_type类型
string.size() // 返回string.size_type,实际上是一个无符号整型数
string +
string cs1 = "hello";
string cs2 = cs1 + "," + "world";
// string cs3 = "hello" + "world" + cs1; // 不能直接使用字面量相加
不能直接使用字面量相加,因为由于历史原因,字符串字面值与string不是相同的类型
string 字符的操作
使用for循环迭代操作string中的字符,注意如果需要修改字符,需要使用引用,如下所示:
string fs("Hello,world");
for (auto &item : fs)
item = toupper(item);
std::cout << fs << std::endl; // 输出HELLO,WORLD
使用索引来访问字符串中的元素,需要注意的是,索引的类型也是 string::size_type
在c++标准中并不要求检查下标是否合法,如果一个下标超出了范围,则可能有不可预知的后果。
vector的初始化
c++提供多种初始化方式
vector<int> v1 = {1,2,3} // 拷贝初始化
vector<int> v2 {1, 2, 3} // 列表初始化
vector<int> v3(10, 1) //初始化10个1
vector的长度: size函数返回的是vector对象中元素的个数,类型是vector<xxx>:size_type类型
试图用下标的形式访问一个不存在的元素将引发错误,不过这种错误不会被编译器发现,而是在运行时产生一个不可预知的错误。(例如缓冲区溢出(buffer overflow))
迭代器
使用迭代器:使用begin和end函数返回迭代器,其中begin成员负责返回指向第一个元素的迭代器,end成员函数返回指向容器(或string对象)尾元素的下一位置。
迭代器的*iter操作返回的是迭代器iter的引用??是否可以将iter本身理解为引用?
string sv = "some thing";
if(sv.begin() != sv.end()){
auto it = sv.begin();
*it = toupper(*it); // 使用*操作符解引用
}
泛型编程:
在c++的for循环中,经常使用!=来替代java中的<=作为判断是否跳出循环,这个是因为在c++的标准库中,很多的容器类提供了!=的运算符而没有<运算符,而很多时候又使用iterator来遍历容器。
迭代器类型
如size_type一样,我们一般不关心迭代器的具体类型。可以是iterator或者const_iterator。
使用新标准中提供的cbegin()和cend(),返回const类型的iterator
Dereference和member
以(*iter).empty为例,*iter的括号是必须的。如果没有括号的话将会被解析为iter.empty member,而iter是一个迭代器没有empty member。
于此同时,c++中提供了->运算符,这个就等于(*it).
使用iterator的不能使用iterator.add()来添加元素到容器中。
vector和string提供了额外的操作,如下表所示:
使用iterator的减法返回有符号数类型的difference_type
值得注意的是,迭代器只定义了减法运算而没有加法运算。
Array
字符数组
由于字符串由'\0'结束,所以在初始化char型数组时需要比字面量空间更大。
数组的初始化必须是一个初始化列表,不能用一个数组给另一个数组赋值,如下:
int[] a = {1, 2, 3};
int[] a2 = a; // Error,不能使用数组赋值
复杂的数组声明
int (*Parray)[10] = &arr; // Parray是一个指针,指向大小为10的数组,数组类型为int
int (&arrRef)[10] = arr; //arrayRef是一个引用,引用的对象是一个大小为10的int类型的数组
在理解复杂的数组声明时,使用由内而外的理解方法去理解。
Eg: int *(&array)[10] = ptrs; // array是一个引用,引用的对象是10个int类型的指针(Reference to an array of ten pointers)
通过下标访问数组
通常使用sizt_t类型来定义数组的长度。
数组和指针
一般情况下,我们使用数组,编译器会自动将指针指向数组的第一个元素。
string nums[] = {"1", "2", "3"};
string *p = &nums[0];
string *p2 = nums; //equivalent to p2 = &nums[0]
使用auto和decltype的类型:
int ia = [1, 2, 3];
auto ia2(ia2);
auto ia2(&ia[0]); // ia2的类型是int*
C++11中引入了新的获取起始指针和尾指针的函数:begin()和end(),包含在iterator header
两个指针相减的结果是ptrdiff_t,类似于size_t,定义在cstddef header中。
可以使用指针来操作数组,参照如下例子:
int *p = &ia[2]; // p points to the element indexed by 2
int j = p[1]; // p[1] is equivalent to *(p+1)
int k = p[-2]; // equivalent to ia[0],可以使用减法指向之前的元素
内建的数组下标可以是负数,而vector等类型的下标必须是无符号数
c语言中的string
在c标准库中定义了一种字符串的convention,以'\0'结尾,定义在<string.h>中(对应c++中的cstring)
并且c标准库中提供的函数并不会校验传入的字符数组是否合法,这可能会引发一些问题。
对于指针来说,如果指针指向的不是同一个对象,那么指针之间的比较就没有意义。
c语言中的字符串示例:
const char ca1[] = "Hello";
const char ca2[] = "World";
if(ca1 < ca2) // undifined comparison。由于实际上是指向不同对象的两个指针做比较
c语言字符串和c++字符串之间的互相使用:
可以使用c语言中的null itermiated char数组,用于初始化字符串
可以将c语言中的字符串用作操作数
c++中提供c_str()成员函数,用string初始化char[]
第4章:表达式
基础
左值和右值
一个左值表达式的求值结果是一个对象或者一个函数。
当一个对象被用作右值时,使用的是对象的值,当一个对象用作左值时,使用的是对象的地址(在内存中的位置)
值溢出
short short_value = 32767; // max value
short_value ++;
std::cout << "short_value:" << short_value << std::endl; // 发生了溢出,不同的系统上结果可能不一样(设置可能直接崩溃),值得记录的是,java的现象跟c++是一致的。运行结果是-32768
c++中支持使用:not来作为非条件。
第5章: Statement
第6章: 函数
try 块
如果抛出了异常而且没有合适的catch处理,则最终执行terminate函数,具体的处理方法与系统相关
参数
const参数
在函数定义时,使用const和不使用const是一样的,因为使用const,顶层的const会被忽略掉。
关于const指针、引用参数的一些例子:
int i = 42;
const int *cp = &i; //正确,但是不能用cp改变i的值
const int &r = i; //正确,但是不能用r修改i的值
const int &r2 = 42; //正确,指向常量
int *p = cp; // 错误,指针类型不匹配
int &r3 = r; // 错误,类型不匹配
int &r4 = 42; // 错误,不能指向常量
可变形参的函数
c++支持initializer_list形参,支持同种类型不同个数的参数
同时支持可变长参数,但是一般仅仅用于和c语言交互的接口,因为很多对象不能正常的拷贝。
不要返回局部对象的引用或指针,否则将指向不可用的地址空间或对象
返回数组指针
int *p[10] :10个指针的数组
int (*p)[10]:一个指针,指向10个整数数组
返回数组指针的方式:
typedef
声明一个返回数组指针的函数
使用尾指针返回类型(auto func(int i)-> int(*)[10])
使用decltype
引用与之类似
调试帮助
Assert 宏
assert由preprocessor处理而不是编译器,所以使用assert时是直接使用。
NDEBUG预处理变量
当使用
#define NDEBUG
在文件开始处时,代码中的assert将不起作用,同时也可以在命令行中使用-D NDEBUG来设置。
除此之外,还可以使用NDEBUG来编写一些根据NDEBUG的值判断是否执行的代码:
#ifndef NDEBUG
#endif
如果NDEBUG没有定义,则上面块中的代码将被执行。
C++预处理器提供了一些用于调试的变量:
__func__ 当前函数
__FILE__ 当前文件
__LINE__ 当前行
DATE 编译日期
TIME 编译时间
重载
函数重载的选择:所有数学转换优先级都是相等的,如:
void test(float f);
void test(long l);
//调用test(1)时会出现ambiguous
C++中不能将const类型的引用赋值给普通类型的引用(暂时可以助记为初始化常量引用时的限制没有普通引用多)
函数指针
定义类型: bool (*pf)(const string&)
可以直接使用函数指针来调用函数,而不需要进行解引用:
bool (*pf)(const string&);
pf("hello");
(*pf)("hello");
给函数指针赋值时,需要返回值类型和参数类型完全一致才可以赋值,函数指针之间不存在指针的转换
函数指针指向重载函数时,需要指定具体使用的函数,通过确定的变量
函数指针作为参数,可以直接使用函数作为参数,该函数实际上会作为一个指针来处理:
void test(bool pf(const string&));
void test(bool (*pf)(const string&));
或者,也可以使用typedef和decltype来简化代码:
//Func和Func2是函数类型
typedef bool Func(const string&);
typedef decltype(test) Func2;
//Funcp和Funcp2是函数指针类型
typedef bool(*Funcp)(const string&);
typedef decltype(test) *Funcp2;
返回指针类型
//声明函数指针
using F = int(int*); // F是一个函数类型,而不是一个函数指针
using PF = int(*)(int*); // PF是函数指针
//定义函数指针
PF f1(int);
F *f1(int);
//上面的定义等于:
int (*f1(int))(int*);
//可以使用auto和decltype
auto f1(int) -> int(*)(int*)
例子:
string::size_type sumLength(const string&, const string&){
}
string::size_type largerLength(const string&, const string&){
}
decltype(largerLength) *getFunc(const string&); //传入函数名来获取函数指针
Java程序员的C++回归路(一)的更多相关文章
- Java程序员的C++回归路(二)
接前: 之前记录的笔记,终于想起来上传完整. 第7章: 类 定义抽象数据类型 任何对成员对象的访问都可以解释为使用this来访问,即this->member. =default :默认构造函数. ...
- 0~5年一个Java程序员的晋升之路
在程序界流行着一种默认的说法叫“黄金5年”,也就是一个程序员从入职的时候算起,前五年的选择直接影响着整个职业生涯中的职业发展方向和薪资走向,如何走好这5年,彻底从一个刚入行的菜鸟蜕变成可以以不变应万变 ...
- Java程序员的成长之路
阅读本文大概需要 8.2 分钟. tips:虽然题目是写的Java程序员,但对其他语言的开发来说也会有借鉴作用. 本篇介绍的是大体思路,以及每个节点所需要学习的书籍内容,如果大家对详细的技术点有需要, ...
- java程序员从小工到专家成神之路(2020版)
目录 必须掌握的基础知识 1. Git & Github 2. Linux 3. 数据结构和算法 4. HTTP / HTTPS 5. 设计模式 6. 计算机原理 java学习之路 1. 工具 ...
- Java程序员学习之路
1. Java语言基础 谈到Java语 言基础学习的书籍,大家肯定会推荐Bruce Eckel的<Thinking in Java>.它是一本写的相当深刻的技术书籍,Java语言基础部分基 ...
- 【转】java架构师之路:JAVA程序员必看的15本书的电子版下载地址
作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从.我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水 ...
- Java架构师之路:JAVA程序员必看的15本书
作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从.我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水 ...
- Java程序员修炼之路
作者简介:王成委,CSDN知识库特邀编辑,Java高级工程师,熟悉Java编程语言和Oracle数据库.专注于高并发架构设计和大数据存储方向的研究. 我们为什么选择Java 大多数人选择Java可能只 ...
- 分享下对JAVA程序员成长之路的总结<转>
我也搞了几年JAVA了,由于一向懒惰,没有成为大牛,只是一普通程序猿,手痒来给新人分享下从新手成长为老鸟的已见. 首先初识语法的阶段,必须要学会怎么操作对象,操作if和for,操作list set ...
随机推荐
- HDU 4162 Shape Number(字符串,最小表示法)
HDU 4162 题意: 给一个数字串(length <= 300,000),数字由0~7构成,求出一阶差分码,然后输出与该差分码循环同构的最小字典序差分码. 思路: 第一步是将差分码求出:s[ ...
- 【Java入门提高篇】Day10 Java代理——静态代理
今天要介绍的是一个Java中一个很重要的概念--代理. 什么是代理?联系生活想想看,代理似乎并不陌生,最形象的代表便是经纪人,明星一般都有经纪人,经纪人作为中间人,负责代理明星的相关事宜,比如说,有人 ...
- Windows 10 安装 Docker for Windows
Docker for Windows是Docker社区版(CE)应用程序. Docker for Windows安装包包括在Windows系统上运行Docker所需的一切. 本主题介绍了预安装注意事项 ...
- Redis的那些最常见面试问题
随笔:经过长达一周的奔波和面试,电话面试,回首今天终于成功的入职了,总共面试了大概10家公司,包括阿里,京东,IBM等等,京东技术过了,学历因为非统招就被pass了,阿里面了2次电话面试就没下文了,估 ...
- JavaScript类型比较
JavaScript的类型 原始类型: number string boolean null undefined 对象类型: Object function Array Date ... 隐式转换 + ...
- CSS图片文字同行居中
img{ display:inline-block; vertical-align:middle; }
- geoserver集成以及部署arcgis server瓦片数据
关注重点: 一般来说,geoserver是不支持arcgis server格式瓦片数据部署的,至少我本机的geoserver版本(2.8.5)以及之前的版本并没有集成进来,不知道目前官网的最新版是否支 ...
- 502 VS 504
本文同时发表在https://github.com/zhangyachen/zhangyachen.github.io/issues/89 首先看一下概念: 502:作为网关或者代理工作的服务器尝试执 ...
- 点分治X2
1.聪聪可可 点分治板子 然而想那个 t1[1]*t1[2]*2+t1[0]*t1[0]想了好久 就是最基本的组合方法 毕竟(2,5)和(5,2)可是要算两次的 画画图就好了 (不要像我一样盯着大佬们 ...
- bzoj 3717: [PA2014]Pakowanie
Description 你有n个物品和m个包.物品有重量,且不可被分割:包也有各自的容量.要把所有物品装入包中,至少需要几个包? Input 第一行两个整数n,m(1<=n<=24,1&l ...