最近又找了老师聊了一下未来的方向,然后就是又改变了。网络分析不让我继续做了,继续c++。苦逼,都快忘完了,这一次是写成随笔形式,便于下次重新复习。参考用书:c++ primer plus
一、c++简介
什么是程序?
程序=算法+数据
我们都知道c++是面向对象编程的一门语言。其实这不够具体。c++也能够进行结构化编程和泛型编程。首先解释一下这三种模式。
1.结构化编程
这是C语言的特性,但由于c++在C语言的基础上开发出来的,所以也支持这种模式。提出结构化编程,是由于旧式程序的执行顺序很混乱,这样是不利于阅读和理解。结构化编程就可以很好的理解,这主要基于结构化编程的两种特性。
1.将分支限制为一组行为良好的结构。(for 循环。while循环)
2.自顶向下的设计思想。结构化编程技术反映了过程性的编程思想。根据执行的操作来构思一个程序。
*结构化编程是更注重于从算法的角度来组织程序
2.面向对象编程
这是c++语言的特性。与结构化编程不同,oop编程更注重的是数据。oop不像过程性编程那样,试图使问题满足于语言的过程性方法,oop是试图让语言来满足问题的要求。其理念是设计与问题本质相对应的数据格式。类就应运而出。在c++中类是一种规范,它描述了这种新型的数据格式,对象是根据这种规范构造的特定的数据结构。oop程序首先是设计类,这些类准确的描述了程序要处理的东西。从低级(类)到高级(程序)的处理过程叫做自下向上编程。
oop特性:
1.封装2.继承3.多态4.抽象。
这些特性在类中理解更好。
3.泛型编程
它与oop目标相同,即重用代码和抽象通用。但它更强调的是独立于特定的数据类型。c++模板提供了完成这种任务的机制。
二、进入c++
1.变量和常量。
变量是程序员自己在程序中编写的值,常量是字面量,直观的表示出来的值。
int a;//这是一个整型变量,你只知道这是一个整型的值,而不知道具体数值。 100 //这是一个字面量,是常量,可以对变量赋值,能显示的表示为多少。但计算机赋值时,根据你要赋值的 //赋值的变量来进行存储。如果不指定,能存储的最小类型来存储。
C++的基本数据类型。
其实这几种类型并不是像以前教科书上,简单的归纳为每个特定的类型,占据几个字节。而要从计算机的内存角度来考量。首先是内存是用位来存储数据的。字节是内存的度量单位。
1.C++不同的整型使用不同的内存量来存储整数。头文件(climits)中可以具体查询到。(内存量的多少直接决定了这种类型最大能存放的数据的大小)
整型:Short,int,long,long long.(unsigned)
整型字面量:具体的数字,有不同的写法。代表了不同的整型。(l.u.)
符号常量:const。变量和字面量绑定。
2.浮点型:float.double.long double(精度)。可以表示很大的数,也可以表示很小的数。浮点型常量可以用科学记数法来进行表示。浮点型实际存储时以2的几次方来进行存储。
3.字符型:char。
**浮点数需要考虑的是精度问题。精度很重要。小数的存储方式很特别。用科学记数法来表示的。要理解。精度的问题,和存储方式有关,存储方式为一个数,一个幂分开存储。这个数就代表了精度。
**字符型。把这个和整型一起理解。和编码集相关。
三种编码格式(准备知识)
所有的string类都是以C-style字符串为基础的。C-style字符串是字符数组。字符类型有三种编码格式:
l 第一种是单字节字符集(singlebyte character set or SBCS)。
在这种编码格式下,所有字符都只用一个字节表示,ASCII码就是单字节字符。用"0"来表示一个字节的结束。
l 第二种编码格式是多字节字符集(multi-bytecharacter set or MBCS)。
即:支持多字节的字符集。
多字节字符集 (MBCS) 是一种旧的方式以支持无法用单字节表示的字符集(如日文和中文)的方法。 如果执行新开发,对于所有的文本字符串应该使用Unicode,除非最终用户没有看到系统字符串。 MBCS 是传统技术,不建议用于新开发中。
最常见的 MBCS 实现是双字节字符集(DBCS)。在Windows里的MBCS包含两种字符类型:单字节字符(singlebyte characters )和双字节字符(doublebyte characters)。
由于Windows 里使用的多字节字符绝大部分是两个字节长,MBCS常被DBCS(double-byte character set)代替。
一般来说,VisualC++(尤其是 MFC)完全支持 DBCS。
l 第三种编码格式是Unicode。
最初的unicode编码是固定长度的,16位,也就是2两个字节代表一个字符,这样一共可以表示65536个字符。显然,这样要表示各种语言中所有的字符是远远不够的。Unicode4.0规范考虑到了这种情况,定义了一组附加字符编码,附加字符编码采用2个16位来表示,这样最多可以定义1048576个附加字符,目前unicode4.0只定义了45960个附加字符。
Unicode只是一个编码规范,目前实际实现的unicode编码只有三种:UTF-8,UCS-2和UTF-16,三种unicode字符集之间可以按照规范进行转换。
3. MBCS编码,Unicode编码与ACIll编码对比
Unicode编码: | 有三种编码方式 |
MBCS编码: | 多字节编码。 |
ACILL编码: | 单字节编码。 |
类型转换:
类型转换的一个原则就是:宽度宽的不能向宽度窄的数据类型转换。类型转换经常用到的地方:算数运算符,函数传参,赋值语句。
强制类型转换为:(type)var
复合类型:数组、字符串、结构体、枚举、共同体。
1.数组
数组的声明:
数组。
数组的声明很重要。
初始化,
索引
取值和赋值。
字符串
字符数组,字符串常量,string
字符数组可以表示字符串,但并不是每一个字符数组都是字符串。最后一个是不是空字符‘\0’
字符串常量可以为字符数组赋值。字符串常量用双引号表示。后面会有隐藏的空字符。这是c-style的原因。
String是新添加的一个类。用法非常灵活。可以用字符串常量来赋值。字符数组不可以直接给字符串赋值,两者类型完全不同。
关于输入字符串的问题:cin有个成员函数来处理。
Cin.get()
Cin.getline()
两者的区别在于是否保留换行符。可以继续添加理解。
字符数组的函数,拼接,比较。
String就比较直白,可以直接运算。
计算长度方法也有不同。Strlen() 和str.size()
结构体。
关键字 struct
成员运算符(.)
用逗号隔开
初始化,和数组差不多。
外部声明
用结构体占位。冒号表示
C语言中的结构体和c++中的结构体是有一点不同的。必须用typedef。
https://www.cnblogs.com/qiumingcheng/p/7814970.html
函数
l 定义
l 原型
l 调用
定义:有无返回值,分为两种,一,void二,除数组外的其他类型。
原型:与编译器的接口。告诉编译器怎么做。
调用:形参para,实参argu。通常是按值传递,即传递一个副本。
数组和函数。
在函数传递数组的格式通常是这样的
Int sun(int arr[],int n)
如果不要修改,需要添加const
数组参数,本质上也是按值传递的。
函数与二维数组。
Int f(int a[][size],int n)
Size是必须的。这样可以很清楚的表达出意思。
函数与字符串。
F(char []),不用标识数组长度,字符串最后会有空值。
函数与string
F(string n)与其他基本类型差不多。
函数与结构体
与其他类型差不多。
函数与array对象。
函数指针。。。
声明函数指针为:type (*pf)(pram) double (*pf)(int)
**在这里书中有个特别强调的地方:const 和指针。int * const pt and const int * pt的区别。
第一个是pt(指针)不能修改。
第二个是指针指向的数据不能修改。还有重要的一点,用const声明的常量不能用非const的指针来指向。
内联函数:
使用相应的函数代码替换函数调用。在函数声明前加上inline。在函数定义前加上加上inline。
引用变量:已定义变量的别名。
1.创建引用变量。
int a; int & b=a;
需要注意的几点:声明引用时必须初始化。这也就意味着,一旦与某个变量关联起来,就一直效忠于这个变量。
2.将引用作为函数参数。
这是C++ 对C的超越。因为C语言只能按值传递。
引用形式
void a(int & a ,int &b)
这里需要注意的是:函数调用时,实参不要是表达式。否则会出现问题。
返回引用时,要注意:要避免返回一个局部变量的引用。
int & f(int & b){return b;}
3.将引用用于结构
引用非常适用于结构和类。设计引用的目的就是为了用于这些类型。
struct a{}; void f(a & t){};//可以修改 void f(const a & t)//不可以修改。
4.将引用用于对象
类对象传递给函数时,c++通常是使用引用。
string对象操作示例:
string & vesiona(string & a,string & b){ return a; } versiona("abc","baf");
这里需要注意一点就是:C-style字符串和string之间有个转换过程。
对象、继承和引用。
基类引用可以指向派生类对象。即如果在函数声明中是一个基类的引用。则可以用派生类对象作为参数。例如参数类型为 ostream &d的函数,可以接受cout对象。也可以接受ofstream对象。
函数中的默认参数
函数重载:并不区分const和非const变量。
函数模板:
template <typename AnyType>
void swap(AnyType & a){}
这里有显示具体化,可以了解一下。
内存模型和名称空间:
1.编译时,需要注意的点:程序一般分为三个部分:头文件、源代码文件(代码总体结构)、源代码文件(调用的函数)。头文件一般包含:函数原型,符号常量,结构、类、模板声明,内联函数。
需要注意的:不要将头文件加入项目列表中,也不要在源码中 使用include来包含源代码文件。
同一个文件中只能包含一个头文件一次,所以头文件中要用这行代码来进行处理:
#ifndef A_H_ #define A_H_ ... #endif
2.存储持续性:
自动存储持续性:函数定义的变量为自动。执行时被创建,执行完被释放。**栈的特性。register:指出定义的变量是自动的。
静态存储持续性:函数外定义的变量或者用static定义的为静态存储,一直存在。static来声明。默认值为0。函数内的为静态局部变量,无链接性。函数外用static的是全局,并且链接性为内部。不用static的为静态全局、外部链接性。
动态存储持续性:用new创建的。被称为自由存储或堆。
线程存储持续性:生命周期和线程一样长。用关键字thread_local来声明的。
3.作用域和链接
作用域:名称多大范围内可见。函数中的变量只能在函数中可见。文件中对文件内函数都可见。
作用域局部变量,只在代码块({})内有效。函数原型的只在参数列表中可见,因此可以省略。类中成员对整个类可见。名称空间,在整个名称空间内可见。
C++函数的作用域是整个类或整个名称空间的,否则不能调用。
链接:描述了名称如何在不同单位之间共享。链接性为外部的可以在文件间共享。自动变量没有链接性。
链接性为外部的:在一个文件中定义,在另一个文件中用引用声明,extern
1.cpp
int a
2.cpp
extern int a
作用域解析运算符:(::)
说明符和限定符
说明符:auto (C++11 delete) register static extern thread_local mutable(用来指出被const限定后,仍然可以修改的数据)
const 全局变量链接性为内部的,和使用了static说明符类似。
限定符:const volatile(即使程序代码没有改变,值仍然有可能发生改变,硬件方面的改变。)
函数和链接性:
也可以用static来限制链接性。
常规new运算符和定位new运算符。
名称空间:声明自己的名称空间:
namespace myth{ int a; double b; }
使用名称空间:
using myth::a; using std::cin
using namespace std;
类与对象:
类:我们为什么要使用类?这是因为在处理一些复杂的问题时,我们从问题的本身来思考如何进行编程。从数据的角度来进行组织代码。从基本数据类型和复合类型中,我们可以看出:一种数据类型说明了三个方面:
1.类型决定了数据的存储量。
2.决定了如何解释内存中的位(float和long占据的位相同,但转换为数值的方式不同)
3.决定了可以进行的操作和方法。
类就是我们自定义的一种类型。我们可以自己定义上面几点。
class a{ int a; double b; char * pt; }
class A{ //默认是 private public: A(); protect: }
类的成员函数:可以在类中声明。在类外定义,但此时要注意(::)。内联函数注意添加关键词inline(定义时。)
类的构造函数和析构函数:const ,利用const声明对象时,成员函数在后面加const的才可以调用。因此我们可以约定,只要类方法不修改对象的值,就可以将其声明为const。
this指针:this的应用。*this。
类作用域。
类中常量的声明:不能用const,因为类声明只是描述了形式,但没有创建对象。解决的方法有:
1.在类中声明一个枚举。2.使用关键词static。但这个常量会被所有对象共享。从这个可以利用 class 来进行枚举。可以使枚举量重名。但这样就不能隐式转换为整型。
用类实现抽象数据类型。
运算符重载:operator 关键词。这与友元函数有联系。可以利用友元函数来进行运算符重载。友元函数将原型放在类声明中,并使用关键字 friend。
类的自动转换和强制类型转换。构造函数可以将某种类型转换为类类型。转换函数进行相反的操作。
类的虚函数和纯虚函数(抽象类):运行中,动态绑定。
虚函数的声明:在基类中,用关键词virtual来声明。
纯虚函数:就是没有定义,在类中,代码格式为:
virtual void func()=0;
使用场景:如果你希望派生类重新定义一个成员函数,那么你应该在基类中把此函数设为virtual。
多态:以单一指令调用不同函数。这就是利用虚函数实现的。(基类指针调用派生类方法)。
********为什么要把基类指针可以指向派生类对象。这样做的目的,是为了实现多态,给不同派生类对象传递相同的信息,可以产生不同的行为。为了实现这种思想,添加了虚函数。虚函数的意义就是基类中没有实现,在派生类实现。而我们用基类声明的指针,可以指向派生类的对象,调用成员函数时,同名函数就会调用派生类的虚函数。9999999999999999999999999999999999999999999999999
如果抽象类的函数不打算被使用,我们就不应该定义它,使用纯虚函数。即拥有纯虚函数的类是抽象类。
抽象类不能产生对象实例。但是我们可以利用抽象类的指针,以便于操作抽象类的各个派生类。
虚函数派生下去,仍然是虚函数,因此派生类中可以省略virtual关键字。