Math.pow(1.1, Day)

Cpp notes

仅仅是流水账式的记录。

###先来一个demo

#include <iostream>

int main(){
    std::cout << "Hi Gril" << std::endl ;
    std::cout << "ret is " << (n1 + n2) << "apple" << std::endl;
    std::cin.get();
    return 0;
}
  • std表示在该命名空间下
  • cout是console out的缩写,表示一个流,一个用来传输数据的通道,后面的<<符号,表示把后面的内容插入到流中。
  • std::endl 表示把它前面的内容先输出,然后将光标移动到下一行,可以在字符串尾部加入\n来达到同样的效果
  • cin.get(),cin表示console in,等待键盘输入

###编译cpp

//mac下使用内置的make编译(记住不是make mian.cpp)
make main
//或者
g++ -o outfile main.cpp

###变量

数值数据类型有:

类型 字节长度 取值范围
short 2 小整数, 无符号:-32768~32767,有符号:[0, 2^16)
int 2,4 整数,取值与short和long相同
long 4 大整数,有符号:[0, 2^32), …

整数分为有符号和无符号,浮点数还有float和double float,它没有无符号的说法。用到的时候查手册。

注意:这些类型的内存需求量可能因os和计算机平台的不同而不同。

int是标准的数据类型,所以下面两行等价:

unsigned int number;
unsigned number;

###格式化数值

//make sure输出的流中的数值最多只能包含四个数字(四舍五入)
std::cout.precision(4)    
//上面的方法是限制数值所有部分的位数,如果我们只要限制小数部分的位数可以如下设置 
std::cout.setf(std::ios::fiexed);
std::cout.setf(std::ios::showpoint);
std::cout.precision(3);
std::cout << 2.3459 ; //2.346

第一句表示使用定点记号,第二句表示不能省略小数点。另外,在为数值设置输出精度的时候,还能设置输出宽度:

std::cout.fill('*');
setw(10);
std::cout << '-' << 1.1 << '-' ;

###类型转换

隐式转换,把某一个类型的数值赋值给另一个类型的时候(不推荐):

int a = 0;
float b = 8.2 ;
a = b ; //a is 8
b = a ; //b is 8.0000

另一种是显示转换,也叫强制转换:

float price = 1.23 ;
std::cout << int (price) ;
//此时输出的是1,但是price的值还是原来的

注意:从实数到整数的转换不做舍入处理。比如:3.8会变为3。如果要四舍五入,可以手动加个0.5,之前在OC里面做数据转换就是这么干的。

更为复杂的类型转换(指针和对象):

static_cast, const_cast,reinterpret_cast, dynamic_cast
//format
operator <type> (var)
int km = static_cast<int>(x+0.5);

单字符串:

char str ;
str = 'A';

char本质是一个小整数,只占用一个字节,取值为:[0,2^8)。cpp使用char类型以数值格式保存字符,这些字符通常来自ascii字符集,但在某些机器上或是国际化环境里可能有变化。在ascii字符集里面,下面两个语句是等价的:

char word = "J";
char word = 74 ;

ascii还包含特殊字符,比如换行符等。可以只用类型转换来查看对应的整数值。

char tab = '\t' ;
std::cout << int (tab) ;

除法

CPP中,整数相除结果还是整数,所以要正确计算除法,一定要用实数,只要参与计算的两个数中一个是浮点数,结果也会是浮点数

float a=3,b=2.0;
float c ; 
c = a/b; //1.5

注意:

  • 另外%只能用于整数。
  • 实数类型存储的知识一个近似值(2.0存储为1.9999),所以浮点数的结果不精确,不要去比较浮点数是否相等

注意:因为char本质是一个整数,所以,可以直接对它进行算术运算:

char temp = 'A'
temp = temp +1 ; //temp is B

###字符串声明和常量

std::string str1, str2;	
str.size()

//常量
const int PI = 3.14 ;

另外,cpp里面还可以使用一个预处理指定来定义(编译器不对预处理做验证)

#define PI 3.14

###操作符和控制结构

####if条件

支持三元选择符 cond ? a : b ; 支持字符串等价判断

####支持swtich case 判断体。

  • 可以多个case对应一个代码块
  • break语句非常重要,只要遇到break就退出switch循环
  • 如果漏掉了break(或者故意没写),程序将继续执行它后面的语句,不管他们是否属于另一个分支。
  • 只能使用整数和char来比较。

支持while和doWhile

数据的溢出处理,比如unsigned short 的65535 + 1 -> 0 ; 0 -1 -> 65535 。具体的和OS有关。 如果是涉及实数处理,结果往往不是翻转,而是变成inf或者infinity,或者NaN

####for循环

变量作用域:for里面初始化的变量只在for里面有效

###输出和输入

前面我们使用cout « 输出了字符流,那么我们也可以使用cin来输入内容

//char用来存储单个字符哈
char str;
//将输入的内容赋值给str
std::cin >> str ;

cin对象在读入一个字符时将忽略空格,所以敲入空格Y回车和只敲入Y回车是一样的效果。如果你想读入任意一个字符,包括各种空白字符,请使用cin的get函数。

//todo 是说使用get可以获取输入的全部字符吗?
char myChar;
std::cin.get(myChar);

####丢弃输入数据

如果在一个代码块中先后使用两次cin,如:cin » xx , cin.get() ; 你会发现在第一次输入字符后,持续会一直执行到结束,而不会出现期待中的——在第二次cin.get()时暂停下来。

cin对象从控制台读入数据时,当用户按下回车时,用户已经输入的数据会一次性发送到Cpp,当cpp收到数据后,就把输入数值赋值,比如你按下的是 Y return ,Cpp会把Y赋值给变量,但return还留在输入流里,这样,当执行到cin.get()时,Cpp会立刻读入那个回车而不是等待你再次按下回车(技术上来说,数据会进入一个由程序为之保留的缓冲区

这个机制在需要需要多次数据的时候会遇到问题。解决方案之一是及时清除多余的缓冲区的数据,可用ignore()。

//len表示要丢失字符的个数
std::cin.ignore(len);
//删除整个缓冲区
std::cin.ignore( std::cin.gcount()+1 )

另一种方案:提供一个表示停止字符的参数,告诉ignore读到什么字符停止执行:

//丢失10个字符 或 遇到第一个换行符
std::cin.ignore(10, '\n');

####输入字符串

定义一个字符串,然后将输入值保存到该变量

//todo 这里申明变量为何要加std namespace?
std::string input ; 
std::cout << 'enter str ';
std::cin >> input ;

这将把用户输入的字符串赋值给input,直到第一个空白字符(空格,制表符,换行,回车都将结束一个输入字符串)

####一次读取多个值

由于cin使用空白字符作为输入str的结束标志,所以Jim Green当被当作两个输入值,我们可以将两个值赋值给两个变量:

std::string firstName, lastName ;
std::cout << 'enter your firstName and lastName' ;
std::cin >> firstName >> lastName ;

注意:两个值的类型可以不同

###读取整行输入

前面的输入都是以空白字符结束,那么问题来了,我要读取整行文字怎么办?我们可以使用getLine函数,使用时,需要将输入了的名字cin当作第一个参数,用来保存的变量当作第二个参数。

std::string sentence; 
std::cout << 'enter a sentence ' ;
std::getLine(std::cin, sentence) ;

getLIne读取输入直到它遇到第一个换行符。换行符本身不会被记录到变量中,地上那个参数定义停止字符,我们让他遇到# 号来停止。

####对输入数据进行检查

cin在检查数据的时候有两个地方容易出问题:一是CIN在调用失败(无法把一个值赋值给变量时)程序仍会像cin调用成功时执行;另外是只有cin接收到了某些数据,程序才会执行,如果用户只是猛按回车而没有输入数值,程序将会一直等在那里。

cin有方法可以检测工作情况,返回布尔值:

  • eof(), 如果到达文件结尾
  • fail(), 如果无法工作
  • bad(), 比较严重的原因(比如内存不足)
  • good(), 如果没有情况

遇到问题时,可以调用clear()清除出错状态,调用ignore()丢失缓冲区数据。

####数据输出到文件

想要对文件进行读写,需要用到stream库。

#include <stream>

创建一个ofstream(output file srtream)类型的变量

std::ofstream fileOutput('fileName');

这行代码把fileOutput变量和一个指定的文件关联在一起。filename可以是相对路径,也可以是绝对路径。使用is_open来检查文件是否被打开。这里比较奇怪的一点是:fileOutput貌似是一个带变量的方法

fileOutput.is_open()
fileOutput.good()

//  写文件:
fileOutput << "write data \n";
fileOutput.close();

注意:fileOutput的默认行为是如没有文件,则创建,如果有,则覆盖。如果要追加数据,需要使用ios::app标志

std::ofstream fileOutput('test.txt' , std::ios::app)

要点:

  • 引入fstream将自动引入iostream
  • 可以将创建一个ofstream变量,然后调用open方法;`std::ofstream fileOutput; fileOutput.open(‘f’)
  • ios::nocraete 和 ios::noreplace ,如果同时使用多个标志,就要用|隔开,如fileOutput('x.txt', (std::ios::nocreate) | (std::ios::noreplace));

一根斜线和两根反斜线的区别(slash , backslash)

  • 非win系统使用/分割文件路径的子目录,比如./代表当前目录
  • win系统使用反斜线来分割路径,蛋因为反斜线在Cpp里面有特殊的含义,用来引导转义序列,如\n,所以必须使用两个\\来才能正确的引用目录。如C:\\windows\\

####使用文件输入

读取文件需要fstream库。

#include //申明一个ifstream(input file stream)类型变量 std::ifstream fileInput('test.txt') if (fileInput.is_open()) { while( std::getLine(fileInput, line) { //do sth } }

默认情况,getLine会读取字符,直到遇到换行。最后别忘了关闭文件。

###5函数

####定义个人函数

首先定义函数原型(放在最前面,方便编译器检查)

//可选参数必须放在最后
void say( int A , int B = 100);

然后定义函数体:

void say (int A , int B) {
    cout << 'hello';
}

使用inline关键字在main之前创建内联函数。

inline void FN(){ }

####函数重载

用同样的名字再定义一个有不同参数(个数,类型),但有着同样用途的函数。

使用场景:需要对于不同类型参数做同样处理的场合。

void say(int A);
void say(char A);

####作用域

  • 在代码块里面申明的只能在代码块里面使用(比如if或者循环语句)
  • 函数里面申明的变量

函数内部的静态变量和非静态变量的区别:他们的值在程序的生命周期内及时多次被调用也不会自动重置

void fn() {
    static int count =1;
    count++;
}

比如上面的代码,第一次调用时count是1,结束时是2;第二次调用是count的值是2,结束时是3。如果没有用static关键字,每次调用fn函数值,count的值都是1. (JS中都是这种方式)

###6复杂的数据类型 ####数组

不同把不同类型的数据放同一个数组

float arrNumber(10);
int nums[] = {1,2,3};

####指针

查看指针地址: &var 指针就是用来存放内存地址的特殊变量:type *pointName 其类型必须和保存地址的指一致。

通过指针访问数据:*var 对于数字,变量的内存地址是指向base地址的(第一个元素的地址),所用*point返回的是数字第一个元素的值,如果要访问其他元素,就需要对指针做运算了。

####结构

struct sName {
    type varName;
}

当处理一个包含多种属性的数据,结构是一个不错的选择。

struct employee {
    shotr sex;
    int age;
}

使用

employee el ; 
el.age ; 
el.sex 

//init
employee el = {18, 'male'} 

####指向结构的指针

struct person {
    int age;
    char gender ;
}
person Jim = {20, 'M'};
//创建一个指向该结构的指针, JimGreen的值为Jim的内存地址
person *JimGreen = &Jim ;
//通过指针来访问结构的变量
(*JimGreen).gender ; 
JimGreen->gender ;

cpp中可以在定义结构后面立即创建该类型的变量:

struct person {
    int age;
} Jim, Lilei ;

还可以创建一个以结构为元素的数组:

person group[10] ;

另一种类型:联合(union)。和结构相似,但他每次只能存储这些之中的一个。如:某个联合里面有三个变量ABC,我先给A赋值,如果再给B赋值,那么A的值将丢失。

####传地址给函数

void changeVar (int *myVar){
    //对指针解引用
    *myVar = 123;
}
int num = 1 ;
changeVar(&num);

###以引用传递的方式传递参数

解决上述方案臃肿的语法:

void changeVar(int &myVar){
    myVar = 123;
}

在定义变量时加&,myVar不是指针,而是那个将被传递变量的一个别名。在函数里面的操作将会反应在原始变量上。如,下面两个变量指向同一个内存:

int n1 = 11;
int &n2 = n1 ;

两个概念:typedef 和 enum

typedef int * intPointer ;
intPointer myVal;

###7 对象

####类 在类的申明里面,要加上public,private和protected三个词之一。标识属性的可访问性

在类中无法给常量赋值,但可以通过创建静态常量来绕开:

class Car {
    public:
        static const float Tank = 12 ;
}

####定义构造器

  • 构造函数的名字和它所属类的名字相同
  • 实例化后立即自动调用这个类的构造函数
  • 它不返回任何值

创建构造器

class Person{
        char gender;
        Person(char g);
    }
//定义构造函数
Person::Person(char g){
    gender = g;
}

一个类可以有多个构造器,名字相同,只是输入参数和类型有差异,创建对象时,程序会根据参数的个数和类型去挑选一个正确的构造器。

析构函数

在销毁一个对象时,相同会调用一个特殊的方法——析构函数。析构和构造是相反的关系。和构造函数同名,前面多一个~

class Person {
    char gender;
    Person(char g);
    ~Person();
}

####this

cpp中的对象是一种特殊的结构,包含变量和函数等,运行时,这些都放在内存中,而this就指向对象本身的内存地址,所以可以通过this来访问对象内部的属性。

###8 继承

创建子类时,基类的构造器先被调用,然后调用子类自身的构造函数。 继承下的析构什么时候发生

####重载方法 cpp允许两个同名的方法共存(参数不同),在调用时,会根据参数来执行对应的方法 所以,对方法进行覆盖的时候一定要注意保持参数相同(类型,个数),不然就会当作重载了。

友元关系。是类/函数之间的一种关系。允许友元访问对方的私有属性。添加友元:

class X : public baseObj {
    public:
        friend class Y
}
//这样Y就要访问X的私有属性了

###9 OOP

####静态属性和方法

静态属性在所有实例中共享, 使用时一定要手动分配内存。

static int count;
//allocate memory
int Pet::count = 0 ;

静态方法尽量通过类来访问: classX::methodName()

####虚函数

直接创建一个指针

int* agePt = new int ;
//通过解引用赋值
*agePt = 18;
delete agePt ;

简单理解:帮助子类覆盖基类的方法

编译时(compile-time),cat和dog都是pet类型的指针,而pet拥有play方法, 所以cat的dog的play都对应pet的play方法 运行时(runtime),cat和dog都是cat和dog类型的指针,于编译时不同,所以我们需要告诉编译器,让子类在运行时根据其 类型调用正确的方法。所以我们就需要在基类中申明为虚方法,见9.2.2

虚构函数都是虚函数(既然这样的化,何必要 virtual ~Game(); 直接~Game()就好了)

####抽象方法

多次继承,在基类中不实现该方法,子类调用时去实现

virtual void play()=0;

有一个抽象方法,还必须有一个普通的虚函数,否则会报错

####重载操作符

newType operator+ (newType rhs);

####多重继承

人, 老师 学生,主角
class zhujiao : public teacher , public student {}

####虚继承

class Teacher : virtual publis Person {}

保证teacher的子类只能拥有一份Person属性。 在使用多重继承的时候,一定要注意继承了基类的多少个副本。最好的实践:基类中只有抽象函数,没有属性。这称之为接口。

###临时记录I

双冒号

表示成员归属 A::member

表示全局作用域

char a;  //全局变量 
int abc() 
{ 
    char a; 
    a = 'a';  //局部变量 
    ::a = 'b';//全局变量 
} 

定义成员方法

class CA { 
    public: 
      int ca_var; 
      int add(int a, int b); 
      int add(int a); 
}; 

int CA::add(int a, int b) 
{ 
      return a + b; 
} 

###VS配置

安装VA, VSVIM VSVIM的配置命令(为了防止误载入,可以在设置里面只载入vsvimrc格式文件):

:set vimrcpaths?
:set vimrc?