C++笔记
29 Apr 2018编码和二进制
- 十进制小数转为二进制,要不断地乘以2直到为整数:
- 3个二进制位为一个8进制位,4个二进制位为一个16进制位。
- 原码:用0,1来表示正负号。其缺点:1.零的表示不唯一,四则运算符号位要单独处理,运算规则复杂。
- 补码:0的表示唯一,符号位可以作为数值参与运算。一般只考虑负数的原码转换方式:
- 原码 -> 反码 -> 补码:负数的原码最高符号位不变,其他位全部取反,最后整体加1。这个过程叫做求补,补码转回原码只需要再次求补即可。
C++ 变量类型
<font color=#CD5C5C> 变量其实只不过是程序可操作的存储区的名称。</font>C++ 中每个变量都有指定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。
每种数据类型的字节长度和具体编译器有关:
C++ 变量初始化
初始化不是复制,初始化的含义是创建变量时赋予起一个初始值,而赋值的含义是吧对象当前值擦除,而以一个新值来替代。–C++Primer P39
如果初始化时未赋值,那其中的值为内存中的不确定垃圾值。
浮点数在内存中是近似存储的,看是否相等就是比较浮点数差值是否足够小。
运算符优先级
算术(乘加移位) > 逻辑 > 条件 > 赋值
隐式类型转换
关键字 auto 和 decltype
- auto:编译器通过初始值自动推断变量类型
- decltype:定义一个变量j与某一表达式i的类型相同,但并不用i初始化。
decltype(i)j = 2; //j以2作为初始值,类型与i一致。
field, parameters, local variables
- field: 字段,成员变量。声明在类的构造函数和函数之外。只要类的对象拥有被类声明的具体的成员变量。
- parameters: 函数参数
- local variables: 本地变量
- this: 关键字this是类的隐藏参数,是一个指针,表示的是具体对象的内存地址。即
&some_obj = this
类的声明
-
类的声明一般放在
.h
文件里面,.h
文件里不应该出现定义!只有声明允许出现在.h
里面:- extern variables
- function prototypes(函数名后有{}的就是定义,否则是声明)
- class/struct declaration(类和结构没有定义,只有声明)
-
类的声明一般包括成员变量和函数。
-
类是虚的,抽象的,是概念,不是实体。只有实体对象才能拥有变量。而函数是属于类的。
-
#include something
:#
是编译预处理指令,将所要include的内容直接放到该指令所处的位置。#include "xx.h"
:在当前目录中去寻找#include <xx.h>
:在系统头文件目录中去寻找,同#include <xx>
-
一般的编译步骤为:
编译预处理指令 (得到.ii) —> 编译器(得到.s) —> 汇编器(得到.o) —> 链接(链接所有.o得出可执行的.out)
-
类的声明在
.cpp
文件完成预编译时只能出现一次!所以如果有多个.h
文件需要预编译进.cpp
可能需要在.h
中使用标准头文件结构:#ifndef HEADER_FLAG #define HEADER_FLAG //type declaration here... #endif // macro define HEADER_FLAG Completed
类的构造和析构
-
对象在定义出来时编译器不会主动帮助把内存相应的存储位置清空,这个动作需要设计程序时自己进行。所以就需要构造函数。
-
构造函数(constructor):帮助对象被创建出来时(可以同时传入一些参数来帮助初始化)就得到初始化;就像python里的
__init__
。 -
析构函数(destructor):释放掉内存空间中的对象数据,不能有参数。在对象被包含的的第一个scope大括号结束时,对象被析构掉。被相当于python里的
__del__
。
/*class head (.h)*/
class Cat // declare
{
int weight;
public:
Cat(initialWeight); // prototypes(原型) constructor
~Cat(); // prototypes destructor
void grow(int years);
void shout();
};
/*class body (.cpp)*/
Cat::Cat(int initialWeight) // define constructor
{
weight = initialWeight;
printf("Cat::Cat()--this=%p\n",this);
}
Cat::~Cat() // define destructor
{
printf("Cat::Cat()--this=%p\n",this);
}
-
default constructor : 无参数的构造函数;auto default constructor : 带默认参数的构造函数。在C++中,C语言中的struct的声明是允许有函数的:
struct X{int i; float f; char c;}; X x1 = {1, 2.2, 'c'}; X x2 = { {1, 1.1, 'a'},{2, 2.2, 'b'} }; struct Y {float f; int i = a; Y(int a);}; // 此处结构体Y的声明中有构造函数 // Y(int a) 不是default constructor, 因为它有参数 Y y1[] = {Y(1), Y(2), Y(3)}; // 正确 Y y2[2] = {Y(1)}; // 错误!因为Y的对象生成必须使用参数, 数组中第二个默认的Y对象无法生成
动态构造对象
所谓动态构造对象,是操作运算符new
和delete
分配(分配后可用指针访问该内存地址)或删除相应对象的内存空间。
new
运算符返回的是内存地址;delete
声明的时候定义了多个student *r = new Student[10];
,delete的时候就要带上[]
:delete[] r;
先调用r内每个元素的析构,然后回收掉每个元素的空间;delete
之前必须有对应的new
,每个对象只能使用一次delete
;delete
可以安全用在null pointer。
访问限制
- 同一个类的对象之间可以访问private成员变量;
- class的成员变量默认属性为private,struct默认的为public;
- 尽量使用class;
初始化列表
- 类里面的所有成员变量都用initialzation list初始化:
Student::Student(string s):name(s){}
- 父类的构造函数初始化也要放到initialzation list。
组合与继承
-
对象可以作为private的成员变量放到其他的对象之中。而继承是类的层面的构造。
-
父类的private内容可以使用,但不能被修改。
class A{ private: int i; protected: void set(int ii){i = ii;} public: A():i(0){} ~A(){} void print(){} }; class B : public A{ public: void f(){ set(20);//父类protected的(public的也可以),所以可以用 i=30;//错了。父类private,所以不可以用 print(); } }; int main() //client class { B b; b.set(10);//错了,因为是protected的,所以非A子类是不可以用的 b.print(); };
3.如果父类有overload
函数(函数同名,但参数不同),那么子类再写同名函数,父类所有相关同名函数全部被屏蔽掉;其他OOP语言此时的子类函数就是对父类中该函数的override
。
const
- 必须让编译器在编译时知道const的初始值。
char * const q = "haha"; //指针q是const
*q = 'a'; //ok
q++; //error
const char *p = "haha"; //(*p)这块内存空间表示的变量是const
char const *p = "haha"; //(*p)这块内存空间表示的变量是const
*p = 'a'; //error
p++; //ok
-
类的函数不会修改成员变量的话,需要在
.h和.cpp
函数名的后面写上const
-
对象声明前加cosnt说明整个对象的成员变量无法被修改。
class A{ const int i; public: //const的成员变量必须在此处初始化列表中初始化 //而且不能这样用:int array[i]! A():i(0){} void f() {} //== f(A* this) void f() const {}//== f(const A* this)也是一种overload }; int main() { const A a; a.f();//执行这个: void f() const {} return 0; }
多态性
任何一个类如果有virtual
函数,那这个类的对象就会比正常的大一点,该类的析构函数必须也是virtual
的,不然析构对象的时候会调用其父类的析构函数。
含有虚函数的类的对象在内存中,第一位存放的是virtual table
,它指向的是内存另外一个地方虚函数表,虚函数是override
自己的虚函数和没有override
的父类的虚函数。
A a();
B b();//B是Ade subclass
a=b;//只有子类中的共有的数据部分会被复制,虚函数还是保留了a自己的
----------------------------------
A* a=new A();
B* b=new B();
a=b;//这时候a和b指向完全相同的B的对象
----------------------------------
void func(A* p)//指针可以提现多态,指针会找到应该使用的函数
{
p->render();//render是A类及其子类中定义的虚函数
}
B b;
func(&b);
void func(A& a)//引用也可以提现多态,引用其实也是指针
{
a.render();//render是A类及其子类中定义的虚函数
}
B b;
func(b);
静态
-
静态局部变量就是全局变量;
- 静态的成员函数内部使用的变量必须全部是静态的。
- 如果一个类有静态的成员变量声明,那么就要在一个CPP文件中进行定义。
拷贝构造
用对象来初始化构造对象。
class A
{
//默认会自动有一个拷贝构造,会拷贝传入对象所有的成员变量!
A(const A&a)
{
//blabla;
}
}
A func(A a)//此处会拷贝构造一个函数内局部作用的A对象!
{
return a;
}
拷贝构造和赋值运算符:
//以下是拷贝构造
A a=b;
A a(b);
//以下是赋值运算
a=b;
//赋值运算符的重载
class A{
public:
A& operator=(const A&){
}
}
运算符重载
Q
- null pointer?
int *p(null pointer) = int 0;
p=new int;
- 类有定义吗?
细节
-
A a=1; == A a(1);
-
一切常数、字符和字符串都是右值; 简单理解左值就是有变量名。
-
类的成员函数:
const func(int a) const{}
最后这个const指的是this。