win32 Console application:手动在source file中添加main.cpp,以命令行窗口显示运行结果;
extern的作用是通知编译器,在本CPP中要用到某个符号,这个符号可能不在本CPP中定义。它表示在某个CPP文件中,存在这么一个函数/全局变量。
数据封装-结构体:现代程序设计语言的一大特点是能够将各自独立的数据或操作组织成一个整体。过程和函数可以将一组操作封闭成一个整体。在数据处理方面,数组可以将类型相同的一组有序数据封装成一个整体。需要时可以从数组中选出元素,并单独进行操作。也可以将他们作为一个整体,同时进行操作。此外,我们同样需要能够将一组无序的、异质的数据看作一个整体进行操作。在程序设计语言中,这样的一组数据被称为记录。在C++语言中,称为结构体。
同时,C++语言提供了丰富的数据类型,用以约定数据的规格,包括数据的存储空间大小、编码和解码方式以及数据可接受的运算处理。
1 内置数据类型:描述常用的整型、字符型、浮点型数据;
2 复合数据类型:描述常变量、数组、指针、引用等复合数据。
3 定制数据类型:定制我们自己的数据类型,约定数据的规格,包括存储空间大小、编码和解码方式及其可接受运算处理行为。
C++语言为描述数据处理过程提供了丰富的语法工具,包括:
1 描述最基本的数据运算行为:运算符和表达式语句;
2 描述数据处理的控制流程:流程控制语句;
3 描述复杂的输入和输出:输入输出流类库;
4 复杂的数据处理过程:复合语句和控制语句;
C++常见转义字符:\n换行;\r回车;\\反斜杠;\'单引号;\"双引号;\0空字符(NULL);
对于C++应用程序来说,数据主要有两种存储方式,一种是栈存储方式,另一种是堆存储方式。栈存储方式通常用于存储占用空间小、生命周期短的数据,如局部变量和函数参数等。堆存储通常用于存储占用空间大、生命周期长的数据,如静态变量和全局变量等,以及用new运算符开辟出的空间。
变量的4个属性:变量名称、数据类型、变量地址、变量的值;变量地址由系统分配给变量一个内在单元编号。C++可以用&(地址运算符)加变量名称得到一个变量的地址;
C++是面向对象的编程语言。它提供了许多功能,如下所示。
简单(Simple)
机器独立或可移植(Portable)
中级编程语言(Mid-level)
结构化编程语言(Structured)
丰富的程序库(RichLibrary)
内存管理(MemoryManagement)
速度快(FastSpeed)
指针(Pointers)
递归(Recursion)
可扩展(Extensible)
面向对象(ObjectOriented)
基于编译器(Compilerbased)
1.简单
C++是一种简单的语言,它提供了结构化方法(将问题分解成几个小部分),丰富的库函数集,数据类型等。
2.机器独立或移植
与汇编语言不同,C/C++语言程序可以在许多机器中执行,但可能有一点点或没有变化。但它不是平台无关的。
3.中级编程语言
C++也用于做低级编程。它用于开发系统应用程序,如内核,驱动程序等。它还支持高级语言的特性。这就是为什么它被称为中级语言。
4.结构化编程语言
C++是一种结构化编程语言,我们可以使用函数将程序分解为多个小部分。所以,它很容易理解和修改。
5.丰富程序库
C++提供了许多内置函数(程序库),一些常用的功能不用再去重头开始编写,有助于提高开发速度。
6.内存管理
它支持动态内存分配的特性。在C++语言中,可以通过调用free()函数随时释放分配的内存。
7.运行速度快
C++语言的编译和执行时间都非常快。
8.指针功能
C++提供指针的功能。可以通过使用指针直接与内存交互。我们可以使用内存,结构,函数,数组等指针。
9.递归
在C++中,可以调用函数内的函数。它为每个函数提供代码可重用性。
10.可扩展
C++语言是可扩展的,因为它可以比较容易地采用新的功能。
11.面向对象
C++是面向对象的编程(OOP)语言。OOP使开发和维护变得更容易,因为在面向过程的编程语言中,如果代码随着项目规模的增长而增长,就不容易管理。
12.基于编译器
namespace是指标识符的各种可见范围。命名空间用关键字namespace 来定义。命名空间是C++的一种机制,用来把单个标识符下的大量有逻辑联系的程序实体组合到一起。此标识符作为此组群的名字。
C++在说明函数原型时,可以为一个或多个参数指定缺省值。调用此函数时,若缺省某一参数,C++自动以缺省值作为此参数的值。
如:
int special(int x=2, float y=1.5)
调用时可用:
special(5,3.2) //x=5; y=3.2
special(6) //x=6; y=1.5
special( ) //x=2; y=1.5
C++是一种基于编译器的编程语言,意思是没有编译就没有C++程序可以执行。首先,我们需要使用编译器编译程序,然后才能执行这个编译后的程序。
学完C以后进阶到C++的第一本书就是这本CP4。CP4确实是经典,但是它却不适合初学者,我当时读了大概有一半了,但是对很多东西还是一知半解。因为这本书是站在编译器的角度来写的,很多东西非常细节,事无巨细的论述对初学者一点好处也没有。初学者需要快速能先吸引眼球的东西。我比较推崇C++新手能先大致了解C++的各方面特性(注意是大致了解不是去抠细节),先有个感性认识再说,而且这样你也会觉得很有趣,不然那些复杂的细节真的会把你搞晕,让你对C++失去兴趣。
有心要学C++的朋友可以这样:先看一本入门读物,我推荐钱能的《C++程序设计教程》,大致看一遍,习题做一做,然后再把C++高级特性认真再复习一遍(类,继承,多态这三个最重要)。至于这本CP4可以当做参考书或者字典一样用,在语法上遇到什么问题或者编译有问题都可以再这本书里找答案,CP4是非常权威的,毕竟Lippman大大是第一个C++编译器的实现者之一。至于之后的进阶可以看看C++之父的《C++程序设计语言(特别版)》。如果对C++的底层实现有兴趣的话可以看看Lippman的《Inside the C++ Object Model》(深度探索C++对象模型),这本书比较难理解,可能要花点时间。如果想用VC++做开发工具的朋友,侯捷的《深入浅出MFC》一定不能错过,看这书你可能会上瘾的(我就是这样)。孙鑫的VC++视频也可以看看。
另外,如果大家想学好C++,就一定要让自己对C++这门语言有兴趣,如果明明自己不喜欢,千万不要逼自己去学,这样没有好处。一旦你喜欢上了C++,你还怕学不好它吗?再次提醒:第一次接触C++的人千万不要去死扣那些非常细节的东西,一定要先从大局上把握C++,细节问题以后再了解一点也不迟。
伟大的操作和数据打包成为面向对象的“类”概念,第2是运算符重载。
广博如四库全书者有之(The C++ Programming Language、C++ Primer、Thinking in C++),
深奥如重山复水者有之(The Annotated C++ Reference Manual, Inside the C++ Object Model),
细说历史者有之(The Design and Evolution of C++, Ruminations on C++),
独沽一味者有之(Polymorphism in C++),
独树一帜者有之(Design Patterns, Large Scale C++ Software Design, C++ FAQs),
另辟蹊径者有之(Generic Programming and the STL),
程序库大全有之(The C++ Standard Library),
专家经验之累积亦有之(Effective C++, More Effective C++)
显式转换也称为强制类型转换(cast),包括以下列名字命名的强制类型转换操作符:static_cast、dynamic_cast、const_cast 和 reinterpret_cast。
命名的强制类型转换符号的一般形式如下:
cast-name
其中 cast-name 为 static_cast、dynamic_cast、const_cast 和reinterpret_cast 之一,type 为转换的目标类型,而 expression 则是被强制转换的值。强制转换的类型指定了在 expression 上执行某种特定类型的转换。
要理解复合表达式(即含有多个操作符的表达式)就必须先了解优先级、结合性以及操作数的求值次序。每一个操作符都有自己的优先级别和结合性。优先级规定复合表达式中操作符结合的方式,而结合性则决定同一个优先级的操作符如何结合。
大多数操作符没有规定其操作数的求值顺序:由编译器自由选择先计算左操作数还是右操作数。通常,操作数的求值顺序不会影响表达式的结果。但是,如果操作符的两个操作数都与同一个对象相关,而且其中一个操作数改变了该对象的值,则程序将会因此而产生严重的错误——而且这类错误很难发现。
最后,可以使用某种类型编写表达式,而实际需要的是另一类型的值。此时,编译器自动实现类型转换(既可以是内置类型也可以是为类类型而定义的),将特定类型转换为所需的类型。C还提供了强制类型转换显式地将数值转换为所需的数据类型。
associativity(结合性)
决定同一优先级的操作符如何结合。C++的操作符要么是左结合(操作符从左向右结合)要么是右结合(操作符从右向左结合)。
只希望计算文本中元音的总数,而不是每一个元音的个数,则可以这样写:
int vowelCnt = 0; // ... switch (ch) { // any occurrence of a,e,i,o,u increments vowelCnt case 'a': case 'e': case 'i': case 'o': case 'u': ++vowelCnt; break; }
for 语句的语法形式是:
for (init-statement condition; expression)
statement
在()中,三部分中的某一部分可以是空,但分号;不能省略,某一部分可以用逗号操作符连接多个表达式;
do while 语句
在实际应用中,可能会要求程序员编写一个交互程序,为用户实现某种计算。一个简单的例子是:程序提示用户输入两个数,然后输出读入数之和。在输出和值后,程序可以让用户选择是否重复这个过程计算下一个和。
// repeatedly ask user for pair of numbers to sum string rsp; // used in the condition; can't be defined inside the do do { cout << "please enter two values: "; int val1, val2; cin >> val1 >> val2; cout << "The sum of " << val1 << " and " << val2 << " = " << val1 + val2 << "\n\n" << "More? [yes][no] "; cin >> rsp; } while (!rsp.empty() && rsp[0] != 'n');
// backward jump over declaration statement ok begin: int sz = get_size(); if (sz <= 0) { goto begin; }
函数调用做了两件事情:用对应的实参初始化函数的形参(创建变量并赋值),并将控制权转移给被调用函数。主调函数的执行被挂起,被调函数开始执行。函数的运行以形参的(隐式)定义和初始化开始。
函数的按值传递或引用、指针传递,区别在于是否在函数体内改变参数的情形下;
值传递是因为变量之间没有关联。而引用或指针传递是因为引用只是变量的别名,而指针是指向某个变量,所以变量之间有关系,这样在修改函数参数时,没有关联的就没有影响,而有关联的就有影响。
Using (const) References to Avoid Copies
利用 const 引用避免复制
编写一个比较两个 string 对象长度的函数作为例子。这个函数需要访问每个 string 对象的 size,但不必修改这些对象。由于 string 对象可能相当长,所以我们希望避免复制操作。使用 const 引用就可避免复制:
// compare the length of two strings bool isShorter(const string &s1, const string &s2) { return s1.size() < s2.size(); }其每一个形参都是 const string 类型的引用。因为形参是引用,所以不复制实参。又因为形参是 const 引用,所以 isShorter 函数不能使用该引用来修改实参。
变量的作用域也可以理解为变量可以用多久及可以给到哪些空间用。
函数有一个潜在的缺点:调用函数比求解等价表达式要慢得多。在大多数的机器上,调用函数都要做很多工作;调用前要先保存寄存器,并在返回时恢复;复制实参;程序还必须转向一个新位置执行。
inline Functions Avoid Function Call Overhead
inline 函数避免函数调用的开销
阅读和理解函数的调用,要比读一条用等价的条件表达式取代函数调用表达式并解释它的含义要容易得多。
如果需要做任何修改,修改函数要比找出并修改每一处等价表达式容易得多。
使用函数可以确保统一的行为,每个测试都保证以相同的方式实现。
函数可以重用,不必为其他应用重写代码。
了解编译器执行代码时的动作。
在类的定义外面定义成员函数必须指明它们是类的成员:
double Sales_item::avg_price() const
命令空间std是类的包装容器(或者称作“更大的容器”)。
命名空间namespace可以防止命名不重复;
STL的namespace是std,standard的缩写;
目的是为了保证名字不出现歧义性;
如果未用using namespace声明命名空间,则使用STL中的函数和变量时需冠以前缀std::
C++标准库头文件是标准库的外在表现形式,使用标准库的唯一途径就是包含相应的头文件(不需要包含后缀.h)
类的成员相当于双重限定。
A class template defines the layout and operation for an unbounded set of related types.
对一些非常紧密关系的数据类型,类模板定义了它们共同的设计(类的结构)和操作(子函数)等。
在实例化过程中,需要声明一个类型为模板类的对象,即使用所需的具体类型替换通用类型名。
C++程序是使用一组协同工作的工具开发的,这些工具称为编译器和链接器。
编译器和链接器将C++转换成能够运行的形式,它将程序从适合人类阅读的形式(源代码)转换成机器能够运行的形式(机器代码)。编译器生成目标文件,链接器则根据目标文件生成可执行文件。
1 使用文本编辑器创建源代码(.c、.cpp、.h);
2 使用编译器将源代码转换成目标代码(.c、.cpp、.h→.o);
3 使用链接器链接目标文件和必要的库,生成可执行的程序。
创建C++程序时,将链接一个或多个目标文件以及一个或多个库。库是一系列可链接的文件,提供了有用的函数和类,可供您在程序中使用。函数是一个执行任务的代码块,如将两个数相乘或显示文本。类定义了一种新数据类型和相关的函数。
一些编译器如GCC(命令行)将编译与链接合二为一,而一些IDE则提供图形用户界面,将编辑、编译、链接、调试、测试等多个功能就地完成。
auto自动变量是较新的C++版本新增的类型,声明变量时,用auto代替变量类型,由编译器根据初始字面值来判断变量类型。
C++程序的预处理区(preprocessor),预处理区的指令多以#开头,不过它不属于C++语言的一部分,主要是告诉编译器程序要引入C++语言中所定义的头文件或指令。一般最常使用的头文件为<iostream>。在C++语言中,为了避免不同函数库使用相同的标识符名称,而设计了名称空间(namespace)的概念,它也将标准函数库中所定义的函数都封装在各自的函数名称空间中,也就是只有在该名称空间的区域才能看到或使用定义的名称,所以在引用标准函数库的函数时,需要加入下一行的语句:
using namespace std;
或
using std:: 函数库函数名称;
函数的传值调用:直接将参数的数值拿来传递,主程序中的参数和函数中的参数都各自拥有内存的地址,它的作用是避免函数中将参数的值改变后,影响到主程序中参数的值。
函数的传址调用:在声明和定义上的参数前加上&运算符。事实上,是将函数的参数地址指向主程序的参数地址,所以在内存上只有一个参数地址,所以无论是更改主程序或函数内的参数值,都会互相影响到彼此。
一个变量未赋初始值时,它或者是一个垃圾值,或者是一个空值NULL或零值0.一般的全局变量和静态变量是后者;
C++ 98 标准
C++标准第一版,1998年发布。正式名称为ISO/IEC 14882:1998 。
C++ 03 标准
C++标准第二版,2003年发布。正式名称为ISO/IEC 14882:2003。
C++ 11 标准
C++标准第三版,2011年8月12日发布。正式名称为ISO/IEC 14882:2011。
C++11对容器类的方法做了三项主要修改。
首先,新增的右值引用使得能够给容器提供移动语义。其次,由于新增了模板类initilizer_list,因此新增了将initilizer_list作为参数的构造函数和赋值运算符。第三,新增的可变参数模板(variadic template)和函数参数包(parameter pack)使得可以提供就地创建(emplacement)方法。
C++ 14 标准
C++标准第四版,2014年8月18日发布。正式名称为ISO/IEC 14882:2014。
C++14是C++11的增量更新,主要是支持普通函数的返回类型推演,泛型 lambda,扩展的 lambda 捕获,对 constexpr 函数限制的修订,constexpr变量模板化等。
C++11
1.对C++核心语言的扩充;
2.核心语言运行期的强化(右值引用和 move 语义;泛化的常数表达式;对POD定义的修正);
3.核心语言建构期表现的加强(外部模板);
4.核心语言使用性的加强(初始化列表;统一的初始化;类型推导[auto关键字];以范围为基础的 for 循环;Lambda函数与表示法;另一种的函数语法;对象构建的改良;显式虚函数重载;空指针;强类型枚举;角括号;显式类型转换;模板的别名;无限制的;unions)
5.核心语言能力的提升(变长参数模板;新的字符串字面值;用户自定义的字面值;多任务存储器模型;thread-local的存储期限;使用或禁用对象的默认函数;long long int 类型;静态assertion;允许sizeof运算符作用在类型的数据成员上,无需明确的对象;)
6.C++标准程序库的变更(标准库组件的升级;线程支持;多元组类型;散列表;正则表达式;通用智能指针;可扩展的随机数功能;包装引用;多态函数对象包装器;用于元编程的类型属性;用于计算函数对象返回类型的统一方法);
在C++11中,lambda函数参数需要被声明为具体的类型。C++14放宽了这一要求,允许lambda函数参数类型使用类型说明符auto。
auto lambda = [] (auto x, auto y) {return (x + y); }
多次包含防范使用#ifndef、#define和#endif,可避免头文件出现多次包含或递归包含错误,有时还可以提供编译速度。
typedef主要是为复杂的声明定义简单的别名。
结构默认的成员访问是公开的,而类是私有的。
赋值号=:右边的值赋给左边的变量;
函数和类:自定义功能单元和代码重用;
引用函数使得函数引用参数更加方便;
OOP将对象作为基本的程序单元。
每一个对象都能接受数据,处理数据,并能将数据传达给其它对象。
对象的属性、方法、事件;
多文档的框架窗口可以容纳多个子文档。
选项卡式文档:将每个文档放置到单独的选项卡上;
&&和||的短路操作虽然会简化计算过程,但也会带来副作用,如c<0 && i = i+3在c>0时,程序就会跳过 i = i+3。
//变量的文件作用域 #include int i = 11; void main() { int i = 5; cout<<i<<endl; cout<<::i<<endl; }
数据类型是所有程序语言的基础。C++程序的所有功能都是建立在内置于C++语言的基本数据类型基础之上的。数据类型可以告诉数据代表的意义以及程序可以对数据执行的哪些操作,它确定了数据和数据操作在程序中的意义。
在C/C++中,数据类型分为两种,简单类型和结构类型。简单类型包括有整数类型、字符类型、浮点类型、指针类型、枚举类型和void类型等。结构类型包括有数组、字符串、记录和文件等。C/C++的基本数据类型属于简单类型。用户可以创建的所有数据类型都是根据基本类型定义的。
C++语言定义了几种基本类型,分别是字符型、整型、浮点型等。C++还提供了可用于自定义数据类型的机制,标准库正是利用这些机制定义了许多更复杂的类型,例如,可变长字符串string和vector等。此外,程序员还能自己修改已有的基本类型以灵活定制复合数据类型。
C/C++中的基础数据类型分为3种:整数型、浮点型和void型。程序中不能定义void类型的变量,它仅仅是用于说明函数没有返回值或说明指针是无类型的。C++中所有的表达式是都可转换或映射为void类型的。
整型是用来表示没有小数位的数,在C/C++中,用来表示整数、字符和布尔值的算术类型合称为整型。可以用很直接的方式来表示整型字面量,-100、+110、120、8888等。
修饰符是整型的一个重要表示,整型可以加上符号和长短的修饰符来表示不同的存储长度和数值范围。
表示长短的修饰符有short、int、long等。一般来说,short代表半个机器字长;int代表一个机器字长;long代表一个或2个机器字长。
另外,整型还可以是带符号的(signed),也可以是无符号的(unsigned)。带符号的整型可以表示正数也可以表示负数(包括0),而无符号的整型只能表示大于等于0的数。
由于有了前缀,整型就可以细分为char(字符型)、unsigned char(无符号字符型)、signed char(有符号字符型)、int(整型)、unsigned int(无符号整型)、signed int(有符号整型)、short int(短整型)、unsigned short int(无符号短整型)、signed short int(有符号短整型)、long int(长整型)、signed long int(有符号长整型)、unsigned long int(无符号长整型)、bool(布尔型)等13种。
在C++中,变量有且仅有一次定义,而且变量在使用之前必须声明或者定义。
C++程序通常由许多文件组成。为了让多个文件访问相同的变量,C++区分了声明和定义。定义和声明有相同的时候,但声明的主要目的是表明变量的类型和名称(extern),而定义的主要目的是为变量分配存储空间。
作用域也可以理解为一种上下文;
在C++中,每个变量名都与唯一的实体(例如变量,函数和类型等)相关联。尽管有这样的要求,还是可以在程序中多次使用同一个变量名,只要它用在不同的区域中,且通过这些区域可以区分该变量名的不同意义。用来区分变量名的不同意义的区域称为作用域。大多数作用域是用花括号来划定界限的。
C++标示符作用域分为全局作用域和局部作用域。
存储类型是从变量的存在时间(即生存期)来划分变量。变量的存储类型可分为静态存储方式和动态存储方式。对于动态存储变量,当程序运行到该变量处时才为其分配存储空间,当程序运行到该变量所在作用域的结束处时自动收回为其分配的存储空间,因此它的生存期为所在作用域。在程序开始就执行就为其分配存储空间,直到程序结束时,才收回变量的存储空间,这种变量称为静态存储空间,其生命周期为整个程序执行的过程。
在C++中,变量的存储类型有自动类型、寄存器类型、静态类型、外部类型等4种。
(1)自动类型变量(auto)
自动类型只能是局部类型的变量,属于动态存储类型。
(2)静态类型变量(static)
static,即在程序运行的过程中静态变量始终是占用一个存储空间。静态变量只能在他的作用范围内使用,使用局部静态变量是为了在下次调用该函数时,能使用上次调用后得到的该变量的值。
(3)寄存器类型变量(register)
属于动态存储类型,编译器不为寄存器类型的变量分配内存空间,而是直接使用CPU的寄存器。以便提高对这类变量的存取速度。主要用于控制循环次数等不需要长期保存值得变量。
(4)外部类型变量(extern)
外部类型变量必须是全局变量,在C++中,有两种情况需要使用外部类型变量。一种是在同一源程序文件中,当在全局的定义之前使用该变量时,在使用前要对该变量进行外部类型变量声明。另一种是当程序有多个文件组成时,若在一个源文件中要引用在另一个源文件中定义的全局变量,则在引用前必须对所引用的变量进行外部声明。
如果在某文件中定义的全局变量不想被其他文件所调用,则必须将该变量声明为静态全局变量,也就是说,静态全局变量只能供所在的文件使用。
while循环与do-while循环的主要区别在于,如果循环条件语句为false,while语句不会执行循环,而do-while保证循环体执行一次。
C++典型循环语句包括for 循环语句、while 循环语句和do-while 循环语句。其中for 循环语句更多的用于需要遍历的情况,而while 和do-while 循环语句可以用于状态判断并且根据状态执行操作等。
continue 语句只能出现在循环语句中,而break 语句可以出现在switch 语句中,continue 语句不会退出循环,只会终止当次执行循环体并且进入下一次循环体的执行,break 语句会退出循环语句。
预处理程序预先(编译之前)对以#开头的语句进行处理。
宏定义是C++的预处理命令之一,它是一个替换操作,不做计算和表达式求解,不占用内存和编译时间。
在C++中,建议尽量避免使用指针和数组,指针和数组容易产生不可预料的错误。其中一部分是概念上的问题,因为指针用于低级操作,容易产生与繁琐细节相关的错误,而其他错误则源于使用指针的语法规则,特别是声明指针的语法。许多有用的程序都可以不使用数组或指针实现,现代C++程序采用vector类型和迭代器取代一般的数组、采用string类型取代C风格字符串。
面向对象是高级编程语言的特征,它以对象为语言的中心,用消息来进行驱动。(消息也是一个成员函数,只是其有消息映射机制,并会传入一个资源的ID号)
面向过程是一种以过程为中心的编程思想,以算法进行驱动。面向对象是一种以对象为中心的编程思想,以消息进行驱动。面向过程编程语言的组成为:程序=算法+数据,面向对象编程语言的组成为:程序=对象+消息。
数组:数据顺序存储,固定大小;
链表:数据可以随机存储,大小可动态改变。
全局对象的构造函数会在main()函数之前执行。
float x与“零值”比较的if语句
x是一个变量,被定义为float,所以在比较时,零值并不是0,而是以下代码:
if(x>0.000001&&x<-0.000001)
一般来说,对于一个浮点数,不会只定义了不使用。通常我们都会对浮点数进行比较复杂的运算,那么在运算的过程中,就有可能发生四舍五入的情况。所以,对于一个进行了一些可能导致四舍五入的运算的浮点数而言,不能保证它的准确性(当然这里的误差是非常非常小的)。所以,在对浮点数比较的时候,就需要考虑到这些误差,方法之一就是,让浮点数与一段数值范围比较,而不是与一个确定的数值比较。
const关键字至少有下列n个作用:
(1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了。
(2)对指针来说,可以指定指针本身为const;也可以指定指针所指的数据为const,或二者同时指定为const。
(3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值。
(4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量。
(5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”,例如下面的代码:
const classA operator*(const classA& a1,const classA& a2);
operator*的返回结果必须是一个const对象。如果不是,这样的代码也不会编译出错。
预编译就是处理#开头的指令。例如,拷贝#include包含的文件代码,#define宏定义的替换和条件编译等。预编译就是为编译做的预备工作的阶段,主要处理#开始的预编译指令。
预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。
int (*s[10])(int)函数指针数组,将每个指针指向一个int func(int param)的函数。
(1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了“无所指”的引用,程序会进入未知状态。
(2)不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其他尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成内存泄露。
虽然避免返回函数内部new分配的内存的引用,但可以返回new的指针。
指针额外的两个很重要的作用:返回函数内部new分配的内存单元的指针;以及在多态中要使用到指针。
(3)可以返回类成员的引用,但最好是const。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其他属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其他对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。
指针是多少位只要看地址总线的位数即可。80386以后的机子都是32的数据总线。所以指针的位数就是4个字节了。应聘者要对操作系统有很好的了解,尤其是以前老式的386和486等。
(1)const 常量有数据类型,而宏常量没有数据类型。使用的时候,编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生一些意料不到的错误。
(2)有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。
设变量名为var,分别给出bool、int、float指针变量与“零值”比较的if 语句。
bool型变量的0判断完全可以写成if(var==0),而int型变量也可以写成if(!var),指针变量的判断也可以写成if(!var),上述写法虽然程序都能正确运行,但是未能清晰地表达程序的意思。
一般情况下,如果想让if判断一个变量的“真”、“假”,应直接使用if(var)、if(!var),表明其为“逻辑”判断;如果用if判断一个数值型变量(short、int、long等),应该用 if(var==0),表明是与0进行“数值”上的比较;而判断指针则适宜用if(var==NULL),这是一种很好的编程习惯。
浮点型变量并不精确,所以不可将float变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。
bool型变量:if(!var)
int型变量:if(var==0)
float型变量:const float EPSINON = 0.00001;
if ((x >= - EPSINON) && (x <= EPSINON)
指针变量:if(var==NULL)
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。
用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p为指针得到的是一个指针变量的字节数,而不是p所指的内存容量。C++/C语言不能知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。
void,空类型,不指定指针指向一个固定的类型,当赋值给其它指针时,再进行强制类型转换,如:
int a; int *p1; void *p2 = p1; int *p3 = (int *)p2;
枚举类型可以将一些整型常量融合到一起;
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
不管怎么说,回调函数是继承自C语言的。在C++中,应只在与C代码建立接口或与已有的回调接口打交道时,才使用回调函数。除了上述情况,在C++中应使用虚拟方法或仿函数(functor),而不是回调函数。
作用域解析运算符是在指定的名称空间中搜索变量,如果前面为空,则是在全局名称空间中搜索,如果是std,表示是在std这个名称空间中搜索变量,如果是类名,则表示在这个类名中搜索。
枚举:具有特定值集的变量;
*与[]在与类型在一起时是声明,没有类型时是解引用或数组成员;
在C程序中,可以用带参数的宏。宏本身不是函数,但使用起来象函数。用复制宏代码的方式代替函数调用可以提高速度。最大缺点是容易出错。
数组元素的地址是通过数组首地址计算的。如数组的首地址是 1000,则第i 个元素的地址是1000 + i * 每个数组元素所占的空间长度
如执行了p=array,则*p与array[0]是等价的,对该指针可以进行任何有关数组下标的操作。
strcpy(q, "abcde");
assert()宏在标准头文件cassert中
assert()有一个参数,表示断言为真的表达式,预处理器产生测试该断言的代码。如果断言不是真,则在发出一个错误消息后程序会终止。
为什么不同类型的计算机可以运行同一个C++程序,而不同类型的计算机不能运行同一个汇编程序?因为不同类型的计算机上有不同的C++编译器,可以将同一个C++程序编译成不同机器上的机器语言表示的目标程序。而汇编程序仅是机器语言的另一种表现形式。不同类型的计算机有不同的机器语言,也就有不同的汇编语言。
静态链接是指把要调用的函数或者过程直接链接到可执行文件中,成为可执行文件的一部分。换句话说,函数和过程的代码就在程序的.exe文件中,该文件包含了运行时所需要的全部代码。静态链接的缺点是:当多个程序都调用相同函数时,内存中就会存在这个函数的多个复制,这样就浪费了内存资源。
动态链接是相对于静态链接而言的。动态链接调用的函数代码并没有被复制到应用程序的可执行文件中去,而是仅仅在其中加入了所调用函数的描述信息(往往是一些重定位信息)。仅当应用程序被装入内存开始运行时,在操作系统的管理下,才在应用程序与相应的动态链接库(gynamic link library, dll)之间建立链接关系。当要执行.dll文件中的函数时,根据链接产生的重定位信息,操作系统才转去执行.dll文件中相应的函数代码。
静态链接库就是使用.lib文件。
传址的实质也是传值,只是其值是一个地址值,函数体内操作的是地址值指向的数据。地址值可以由指针传递,也可以由引用来传递。
函数模板是自动生成重载函数的方法。
typeid(变量名):可以获取对象的类型信息;使用头文件
因为效率,C++被设计为系统级的编程语言,效率是优先考虑的方向,c++秉持的一个设计哲学是不为不必要的操作付出任何额外的代价,所以它有别于java,不给成员变量和局部变量做默认初始化,如果需要赋初值,那就由程序员自己去保证。
结论:从安全的角度出发,定义变量的时候赋初值是一个好的习惯,很多错误皆因未正确初始化而起,C++11支持成员变量定义的时候直接初始化,成员变量尽量在成员初始化列表里初始化,且要按定义的顺序初始化。
用 struct 定义类型与用 class 定义类型有什么区别?
在结构体中,缺省的访问特性是公有的。在类中,缺省的访问特性是私有的。
在定义一个类时,哪些部分应放在头文件(.h文件)中,哪些部分应放在实现文件(.cpp文件)中?
类的定义放在头文件中,类中成员函数的实现放在实现文件中。
重载后的运算符的优先级和结合性与用于内置类型时有何区别?
与内置类型完全一样。
如何区分++和--的前缀用法和后缀用法的重载函数?
后缀++的重载函数中增加了第二个参数:一个整型的参数。这个参数只是为了区分两个函数,它的值没有任何意义。当程序中出现前缀++时,编译器调用不带整型参数的operator++函数。如果是后缀的++,则调用带整型参数的operator++函数。--重载也是如此。
强制类型转换在C++类型系统中引入了一个漏洞
为了方便查找这些错误,C++提供了在强制类型转换时指名转换的性质。
转换的性质有四种:
静态转换(static_cast):用于编译器隐式执行的任何类型转换
重解释转换(reinterpret_cast)
常量转换(const_cast)
动态转换(dynamic_cast)
格式
转换类型<类型名> (表达式)
z = static_cast
逻辑表达式在执行时,先处理左边。如左边已能决定此逻辑表达式的结果,则右边不执行。因此,在&& 逻辑表达式中,应把false可能性较大的条件放在左边,在||表达式中,应把true可能性较大的条件放在左边,这样可以减少程序执行的时间
设计库中的函数的实现:表现为一个源文件。
库的这种实现方法称为信息隐藏。
计算机的工作是建立在抽象的基础上。
机器语言和汇编语言是对机器硬件的抽象
高级语言是对汇编语言和机器语言的抽象
现有抽象的问题:
要求程序员按计算机的结构去思考,而不是按要解决的问题的结构去思考。
当程序员要解决一个问题时,必须要在机器模型和实际要解决的问题模型之间建立联系。
而计算机的结构本质上还是为了支持计算,当要解决一些非计算问题时,这个联系的建立是很困难的
每个成员函数都有一个隐藏的指向本类型的指针形参this,它指向当前调用成员函数的对象
通常,在写成员函数时可以省略this,编译时会自动加上它们。
如果在成员函数中要把对象作为整体来访问时,必须显式地使用this指针。这种情况常出现在函数中返回一个对调用函数的对象的引用,
某些类的对象,必须在对它进行了初始化以后才能使用。对于某些类的对象在消亡前,往往也需要执行一些操作,做一些善后的处理。
初始化和扫尾的工作给类的用户带来了额外的负担,使他们觉得类和内置类型还是不一样。
用户希望使用类的对象就和使用内置类型的变量一样,一旦定义了,就能直接使用。用完了,由系统自动回收。
对const对象只能调用const成员函数。
任何不修改数据成员的函数都应该声明为const类型。
const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。同一类的不同的对象其const数据成员的值可以不同。
不能在类声明中初始化const数据成员。
const数据成员的初始化只能在类构造函数的初始化表中进行,不能在构造函数中对他赋值。
如果作为类的成员函数,它的形式参数个数比运算符的运算对象数少1。这是因为成员函数有一个隐含的参数this。在C++中,把隐含参数this作为运算符的第一个参数。
设计算法最有效的手段就是将任务分解成多个子任务,再将每个子任务分解成更小的子任务,依此类推。最终,子任务会变得很小,很容易用C++代码实现。这种设计方法称为自顶向下设计。有时也称为逐步求精,或者更形象地称为分而治之。使用自顶向下方法,可将程序的任务分解为一组子任务,并通过子算法来解决这些子任务,从而完成一个程序的设计。
在C++程序里采用这种自顶向下结构,能使程序更容易理解,更容易更改,而且使程序更清晰,更容易编写、测试和调试。和大多数编程语言一样,C++提供了在程序中包括独立的子部分的机制。在其他编程语言里,这些子部分称为子程序、子过程或者方法。在C++里,这些子部分称为函数。通过函数将编程任务分解为子任务的好处在于,不同的人可以负责不同的子任务。构造大型程序(比如编译器或办公管理系统)时,如果程序开发时间有限,这种形式的团队协作就非常重要。
static_cast
函数声明指出写函数调用所需的全部细节。函数声明和函数定义至少要有一样放到函数调用之前。函数声明通常放在程序的main 部分之前。
为了理解函数,请谨记以下3 点。
函数定义如同小程序,调用函数相当于运行这个“小程序”。
函数使用形参(而不是cin)来输入。函数的实参就是实际输入的值,它们在形参位置代入。
函数(本章讨论的这一种)通常不向屏幕发送任何输出,但会将一种“输出”送还给程序。函数会返回一个值,该值好比函数的“输出”。函数进行这样的输出是用return 语句,而不是用cout 语句。
函数声明通常放到程序的main 部分之前,函数定义通常放到程序的main 部分之后(或者放到单独文件中)。这样也更有可读性。
“流”是第一个“对象”。对象乃是一种特殊变量,拥有自己的专用函数。某种意义上,这些函数是“附加”到变量上的。
对象是关联了函数的变量。这些函数称为成员函数。类是一种类型,它的变量就是对象。对象所属的类(即对象的类型)决定了对象有哪些成员函数。
使用数组的索引变量与使用数组基类型的其他任何变量没有区别。因为其名字也是内存单元的命名。
数组的索引变量在计算机内存中一个接一个地存储,所以整个数组会占用一个连续的内存存储区域。数组作为参数传给函数时,只有第一个索引变量(索引编号0)的地址才会传给调用函数。所以,使用数组形参的函数通常还需要另一个int 类型的形参来给出数组的长度。
指针是变量的内存地址。计算机内存被划分为许多编好号的内存位置(称为字节),变量被实现为一组相邻的内存位置。C++系统有时将内存地址作为变量名使用。假定变量用3 个内存位置实现,就可以将第一个位置的地址作为变量名使用。例如,假定变量作为传引用参数使用,传给调用函数的就是这个地址,而非变量的标识符名称。
用变量起始处的内存地址命名变量,该地址就称为指针,因为可以认为该地址“指向”变量。之所以说地址“指向”变量,是因为是用变量的位置(而不是变量名)标识变量。例如,在提到位于位置编号1007 的变量时,可以说:“它是在位置1007 的变量。”前面许多地方用过指针。如前所述,在函数调用中将变量作为传引用参数使用时,是以指针形式向函数提供实参。这是指针的一个重要而且强大的用途,但具体过程是由C++系统自动完成的。
操作符delete 可销毁动态变量,并将其占用的内存还给自由存储,使内存得以重用。假定p 是指向动态变量的指针变量,以下语句会销毁p 指向的动态变量,将占用的内存还给自由存储:delete p;像这样调用delete,p 的值会进入未定义状态,现在的p 应被视为一个未初始化变量。
如变量是函数的局部变量,调用函数时变量会由C++系统创建,并在函数调用结束时销毁。由于程序的main 部分本质上是一个名为main 的函数,所以在main 部分中声明的变量也会这样处理。由于对main 的调用直到程序终止才结束,所以main 中声明的变量直到程序终止才销毁。无论main 还是其他函数,局部变量都这样处理。我们过去用过的普通变量(也就是在main 或者其他函数定义中声明的变量)称为自动变量,因为它们的动态属性由系统自动控制;调用函数时,函数内部声明的变量自动创建,函数调用结束后,自动销毁。我们通常将这些变量称为普通变量,但也有一些书把它们称为自动变量。
操作符::是作用域解析操作符,作用和圆点操作符相似。无论圆点操作符,还是作用域解析操作符,都指出一个成员函数具体是谁的成员。但作用域解析操作符::作用于类名,而圆点操作符作用于对象(类的变量)。作用域解析操作符必须是连续两个冒号,中间不能有空格。操作符前的类名为类型限定符,它使函数名具体化,指出该函数从属(限定)于哪个类型。
圆点操作符和作用域解析操作符
两个操作符都随成员名称一起使用,指出它们具体是谁的成员。例如,假设声明一个名为DayOfYear 的类,并像下面这样声明它的一个名为today 的对象:
DayOfYear today;
那么要用圆点操作符指定today 对象的成员。例如,output 是DayOfYear 类的成员函数(在图10.3 中定义),以下函数调用输出存储在today 对象中的数据值:
today.output();
为成员函数给出函数定义时,则要使用作用域解析操作符::指定类名。例如,成员函数output 的函数定义头是:
void DayOfYear::output()
记住,作用域解析操作符::用于类名,而圆点操作符用于那个类的对象。
冒号:可以在构造函数后跟参数列表。可以在前面跟一个标识,用于跳转的开始处。或者是用在条件运算符中。
类是变量为对象的类型。对象可拥有成员变量和成员函数。
类的友元函数其实就是普通函数,只是能像成员函数那样访问类的私有成员。
如果类有一套完整的取值函数getval()和赋值函数setval(),将函数变成友元的唯一理由就是简化函数定义,并提高它的效率——但这些理由通常就足够了。(是说普通函数不能访问私有成员数据,但可以他用取值函数来访问,这样会影响效率,相对于直接访问私有成员)
对于不被函数更改的类类型参数,通常应将其设为常量参数。
数组基类型可以是结构或类类型。结构或类可将数组作为自己的成员变量。
可为一个类重载赋值操作符=,使它具有你希望的行为。但它必须作为类的成员来重载,不能作为友元。使用了指针和new 操作符的任何类都应重载作用于那个类的赋值操作符。
两个简单的规则可帮你选择成员函数和非成员函数:
函数要执行的任务只涉及一个对象,就使用成员函数。
要执行的任务涉及多个对象,就使用非成员函数。将它作为非成员(友元)函数。
友元函数一般以在参数中引用类对象,当然在定义中引用对象成员也要限定对象。
C++允许将程序分解成多个部分,保存到独立的文件中,独立编译,并在程序运行时(或在运行前)链接到一起。可将类定义(以及相关函数定义)放到一个文件中,使这个文件独立于使用该类的程序。这样就可方便地建立起一个类库,以便多个程序使用相同的类。类只需编译一次,即可在多个程序中使用,和使用预定义库一样。此外,类也可用两个文件来定义,将这个类的使用规范与类的实现细节分开,如果类按规则定义,以后只需重新编译类的实现文件。其他文件(包括使用类的程序文件)不需要更改,甚至不需要重新编译。
就是说接口文件是头文件,做为界面,要保持一定的稳定性,而实现文件,也只是.cpp,用于实现头文件的声明。这样当头文件被调用时,引用的只是头文件的声明。当实现文件做出改变时,并不影响接口被引用。
如果使用数据类型的程序员访问不到值和操作的实现细节,就可将这种数据类型称为抽象数据类型(ADT)。你定义的所有类都应该是ADT。ADT类确保了类的接口和实现的分离,这是良好的编程实践。
常量NULL实际是数字0,对于以下重载函数:
void func(int *p);
void func(int i);
func(NULL)实际调用哪个函数,由于NULL是数字0,所以两者均为有效,C++11的解决方案是引入新常量nullptr。nullptr不是整数零,而是代表空指针的字面值常量。
基类构造函数不被派生类继承,但可在派生类构造函数的定义中调用基类构造函数。
由于有大量标准数据结构用于容纳数据,人们觉得有必要为这些数据结构提供标准的、可移植的实现。
声明(引入名字或作为接口):
一个没有具体说明函数体的函数;
包含一个extern说明符,并且没有初始化程序或函数体;
一个类定义内的静态类数据成员的声明;
一个类名的声明;
一个typedef声明;
定义(实现内存分配):
静态类数据成员;
一个non-inline成员函数。
一个声明将一个名字引入作用域。在C++中,声明和定义的区别在于:在一个给定的作用域中重复一个声明是合法的;相比这下,在程序中使用的每个实体(如类、对象、枚举器、函数等)必须只有一个定义。
一个定义提供了一个实体(例如,类型、实例、函数)在一个程序中的唯一描述。
声明:
int func(int,int); class IntSetIter; typedef int Int; friend IntSetIter; extern int globalVariable;
是声明也是定义:
int x; char *p; extern int globalVariable = 1; static int s_instancecount; static int f(int,int){;} inline int h(int,int{;} enum Color { RED, GREEN, BLUE}; const double DEFAULT_TOLERANCE = 1.0e-6; class Stack {;}; struct Util{;}; templatevoid sort(const T**arr, int size){;}
函数和静态数据成员声明是例外。它们虽然没有定义,但是在一个类的定义中也不能重复:
class Nogood { static int i; public: int f(); };
编译一个.c或.cpp文件时,预处理器首先(递归地)包含头文件,形成包含所有必要信息的单个源文件。之后,编译这个中间文件(称为编译单元)生成一个和根文件名字一样的.o文件(目标文件)。链接程序把各种编译单元内产生的符号链接起来,生成一个可执行程序。有两种不同的链接:内部链接和外部链接。链接类型将会直接影响我们将一个给定的逻辑构造合并到物理设计中。
如果一个名字对于它的编译单元来说是局部的,并且在链接时与在其他编译单元中定义的标识符名称不冲突,那么这个名字有内部链接。
链接意味着对这个定义的访问受到当前编译单元的限制。也就是说,一个有内部链接的定义对于任何其他编译单元都是不可见的,因此在链接过程中不能用来解析未定义的符号。
如果一个名字有外部链接,那么在多文件程序中,在链接时这个名字可以和其他编译单元交互。其标识符在整个程序中必须是唯一的。
在C++中,将一个带有外部链接的定义旋转在一个h文件中往往属于编程错误。
在C++中,在一个头文件的作用域内旋转一个带有内部链接的定义,例如静态函数或者静态数据,是合法的。但不推荐。
头文件中可以包含的内容:
class Radio { static int s_count; static const double s_PI; int d_size; public: int size() const; }; inline int Radio::size() const { return d_size; }
头文件中不应该包括:
extern int LENGTH = 11; int z; const int WIDTH= 5 //void static void func() {;} //void int Radio::s_count; double Radio::SPI = 3.141592; int Radio:: size() const {;}
在标准C++中,指针容纳的是另一个变量或函数的内存地址。这意味着可用指针间接引用变量。
枚举(简称enum)是一组命名的整数常量。枚举适合表示从一组固定值中取其一的类型,比如一周中的日子或者一年中的月份。
dynamic_cast是C++的运行时动态转型机制,类型不匹配会返回一个nullptr。
Various number types have different memory requirements
More precision requires more bytes of memory
Very large numbers require more bytes of memory
Very small numbers require more bytes of memory
键盘、磁盘文件文件、鼠标都是输入设备,C++不可能为每一种输入设备都提供一个专用的输入语句。
cin、cout对象声明在 iosteam中。
对于一个源文件,C++编译器通过编译预处理,将该源文件中的#include包含指令和#define宏定义指令替换掉,从而生成一份完整的源代码文件。该源代码文件就编译器进行编译处理的基本单元,称为一个编译单元(translation unit)。
通过独立编译或分别编译成目标文件(.obj)。
目标文件可以被计算机理解,但是还不能被计算机直接执行,因为在多个目标文件之间可能会共享同一个函数或者变量,需要建立这些共享名字之间的链接关系。此外,目标文件中还缺乏程序执行的启动代码。
链接处理,就利用链接器建立多个目标文件以及c++库文件之间的链接关系,并连同操作系统启动代码,一起打包成一个可执行文件。
C++的头文件和独立编译机制,支持我们将程序代码非常方便自然地组织到多个文件中,非常适合于团队协作编写规模较大的程序。
那么,作为团队成员,我们在编写自己的程序代码时必须清晰地把握:
在我的程序代码中哪些变量或者函数是供自己使用的,不能与其他成员的程序代码冲突;哪些变量和函数是需要与其他成员共享的,应该如何实现它们的的共享。C++语言提供了一系统语法约定,用以避免函数和变量等程序实体的冲突并实现共享。
1 约定程序实体所在的空间范围:作用域;
2 约定程序实体生成的时间范围:生存期;
3 在多文件之间共享程序实体:链接属性;
4 避免全局程序实体的冲突机制:名字空间;
C++规定了四种作用域
1 局部作用域,位置;
2 文件作用域,extern
3 名字空间作用域,::或using namespacing
4 类作用域,::
在局部域中定义或者声明的变量或函数,其右见性从声明点开始,一直延伸到匹配的花括号的结束外。(VC6在for循环中初始化循环变量时,其作用域会超出函数块)
C++约定的局部作用域有三种:复合语句的块域、函数域和函数声明域。
函数参数只是一个占位符,最终总是需被赋值,用常量赋实值,用变量赋实值或地址值或别名,其本身并不存在什么作用域。
所在不能并引用,在调用被赋值或初始化时或是实值或是其外部变量的引用(地址址)或引用的别名。
unsing namespace 名字空间名
using 名字空间名::成员名
using namespace std;
using std::cout;
指针:间接操作其他变量;
引用:直接操作其他变量;
引用不就是一个常量指针吗?
const pointer可以初始化为NULL,而&不行,即引用总是指向某个对象。
1) 引用必须被初始化,指针不必。
2) 引用初始化以后不能被改变,指针可以改变所指的对象。
3) 不存在指向空值的引用,但是存在指向空值的指针。
1 非空的差别任何情况下都不能使用指向空值的引用。一个引用必须总是指向某
2 合法性区别在使用引用之前不需要测试他的合法性。指针必须测试。
3 可修改区别 指针可以被重新赋值给另一个不同的对象。但是引用总是指向在初始化的时候被制定的对象,以后不能改变。但是指定的对象其内容可以改变。
应该使用指针的情况: 可能存在不指向任何对象的可能性,需要在不同的时刻指向不同的对象(此时,你能够改变指针的指向) 。返回函数体中new出的内存空间的地址。多态中使用指针。
应该使用引用的情况: 如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,使用此时应使用引用。
数组用作函数形式参数时会丢失数组元素个数的信息,即退化为指针;
函数返回数组名,实际返回的是指向数组首元素的指针;
界面,函数的函数头,类或对象的声明中的public部分。对象之间通过对象的public方法进行交互,另外,类成员可以是其他对象。
方法可以利用过程化语法工具来完成描述。
一个对象的成员数据也是顺序存储在一起的,或者是按各自长度存储,或者编译器基于效率因素增加一些额外字节以使存储边界对齐。
这样对象名就是这块内存空间的首地址,而成员就是内存空间的偏移。
同样的,通过对象名和成员函数名,也可以定位到其对应的函数地址。
运算符本质上是函数。
通过在构造函数体中给数据成员赋值,或者使用初始化列表进行初始化。两种方式效果相同,但本质不同。
其中,在构造函数的函数体中,对象已经产生出来,只是初始状态不符合我们的要求,因此,我们通过赋值修改对象的状态。
在初始化列表中,对象正在产生,且使用初始化列表设置初始状态。
在一些情况下,对象一旦产生,它的某些数据成员就不允许修改,便即常量数据成员或者引用型数据成员,此时,就必须使用初始化列表的方式。
通常,默认构造函数仅负责创建对象,基本上不做什么初始化工作。
通过默认参数和初始化列表,可以使用灵活的对象定义方式:
CDate::CDate(int y =1900,int m=1,int d=1):year(y),month(m),day(d){}
接口sort()(放在.h文件中),用户用它,而无须了解其是如何实现的,实现者可以更改sort()的实现(放在.cpp中),可以使用快速排序,也可以使用选择排序或归并排序。
virtual是告诉编译器,不要静态链接到该函数,而是在运行时根据基类指针指向动态链接。
您可能想要在基类中定义虚函数,以便在派生类中重新定义该函数更好地适用于对象,但是您在基类中又不能对虚函数给出有意义的实现,这个时候就会用到纯虚函数。
在C++中,我们使用访问标签(private、public、protected)来定义类的抽象接口。一个类可以包含零个或多个访问标签。
析构函数通常在对象生命期结束时被系统自动调用。如,静态对象在程序结束时、局部对象在出了局部域时、临时对象生命期结束时、通过delete删除动态对象时。
在成员函数中访问自身对象成员:可以访问所有成员,直接使用成员名;
在成员函数中访问同类对象成员:可以访问所有成员,使用点号或者箭头语法;
在外部函数中访问对象成员:只能访问公有成员,使用点号或者箭头语法。
自身对象是定义成员函数时假设存在的对象,未来谁调用该成员函数,谁就是自身对象。C++语言提供了一个隐含的this指针专门用于指向自身对象。
在C++中处理C++类需要包含头文件和使用命名空间:
#include <iostream.h>
#include <string>
using namespace std;
以C++中,用new来申请堆内存空间,用delete来释放堆内存空间。
delete [] 指向堆内存空间首元素的地址;
如果只申请了一个堆内存变量,[]也可以省略。
C++在说明函数原型时,可以为一个或多个参数指定缺省值。调用此函数时,若缺省某一参数,C++自动以缺省值作为此参数的值。如:
int special(int x=2, float y=1.5)
调用时可用:
special(5,3.2) //x=5; y=3.2
special(6) //x=6; y=1.5
special( ) //x=2; y=1.5
缺省参数无论有几个,都必须放在参数序列的最后,
在函数调用时,若某个参数省略,则其后的参数皆应省略而取其缺省值
double *p, *q, *t;
p new double;
q = new double(1.0); // q指向的单元被初始化为1.0
t = new double[10]; // 为t分配一个长度为10的一维数组;
如果分配失败,则返回一个空指针;
std::priority_queque:以特定顺序存储元素。因为优先级最高的元素总是位于队列开头。
把模板的实现和声明都写在头文件吧。
很多时候是一种宏替换操作,如define、typedef、inline;
函数重载和函数模板都是为了增加重用,一个是函数名的重用,一个是函数体的重用。
结构体是一组变量的集合,类是一组变量和函数的集合。
集合和元素的名称分别对应内存空间的基地址和偏移地址。
对于类,除了多了函数成员以外,还定义了偏移空间的访问权限。
可以用extern或::在局部域引用外面的变量;
在C++中,赋值与初始化是不一样的,赋值是删除原值,赋予新值,初始化则是开辟空间和初始化同时完成,直接给予一个新值;
有两种方法可用于定义类型别名:传统的方法是使用关键字typedef;C++11中规定了一种新的方法,使用别名声明(alias declaration)来定义类型的别名,即使用using.
关键字typedef作为声明语句中的基本数据类型的一部分出现。含有typedef的声明语句定义的不再是变量而是类型别名。和以前的声明语句一样,这里的声明符也可以包含类型修饰,从而也能由基本数据类型构造出复合类型来。
C++11中用关键字using作为别名声明的开始,其后紧跟别名和等号,其作用是把等号左侧的名字规定成等号右侧类型的别名。
初始化列表和右值都是C++11
在C++中,我们是通过抽象基类(abstract base class)来实现接口与实现分离的。
从某种意义上说,模板只不过是语法宏的一种受限形式。以类型作为参数并产生代码。与普通宏不同,模板足够安全。
string类与vector,后者也不只是字符串。
Pointers and References as Modifiers So far, you’ve seen the basic data types char, int, float, and double, along with the specifiers signed, unsigned, short, and long, which can be used with the basic data types in almost any combination. Now you’ve added pointers and references that are orthogonal to the basic data types and specifiers, so the possible combinations have just tripled.
函数在编译时被分配给一个入口地址。这个入口地址就称为函数的地址,也是函数的指针。
指针数组与二级指针:
char *str[ ]={“China”, “Japan”, “America”};
**str
*(*(str+0)+0)
//C
实际上,线性表相当于一个整型数组,List为数组的首地址,nMax为数组在内存开辟的空间数,nElem为数组中实际存放的元素个数。只不过数组的空间是动态开辟的。
随机数是对某个“初始数”进行某种迭代运算而产生的,如每次取三角函数结果的第N位等。
临时对象的开销比局部对象小些。
编译器从模板生成一个特定的类或函数的过程称为模板的实例化。
对象的列表初始化方法,大大提升了C++中object初始化语法的简易化程序。
并非所有类型都需要销毁实例,它牵涉应用程序的资源管理,主要在有动态内存需要释放时。
C++大胆地运用了类型修正符(type modifier),对原型进行一些修改和限制。主要包括:const、volatile、mutable
const用于规定变量的不可修改性;
mutable用于成员变量,指当声明一个const对象时,如果某个成员是mutable属性的,则该成员变量可以被修改,但是整体的“常量性”不变。
volatile是指当一个变量被优化存储到寄存器中时,在不断从寄存器中调用时,可能在内存中有对其修改,但没有同步更新的寄存器,volatile是告诉编译器,不能优化存储到寄存器中,以免出现更新不同步的错误。
const关键字可以约束普通变量,也可以约束一个对象,使之成为常量对象。例如:
const Rectangle rect(3, 4);
这样一来,对象rect的所有属性都是不可修改的,除非某个属性被说明成是mutable。
常量对象不是很常见。最容易出现的一个场合是函数返回对象的值这一情况。这个返回的对象被编译器自动约束成为常量对象。另一个经常使用常量对象(引用)的场合是对象作为函数的参数。
与无约束对象一样,可以调用常量对象的成员函数来完成某项操作。但这可能带来潜在的错误:这个成员函数可能会修改对象的属性。
类中的枚举成员不属于对象,而是该类的所有对象共享的。因此,对枚举成员的访问必须采用名字限定的方式进行。例如:
Quadrangle::ID id = Quadrangle::RECTANGLE; //OK
id = SQUARE; //error
同样地,如果枚举定义被放在非公有段中,以上访问如果发生在Quadrangle类之外就是非法的。
声明和定义包括
1 变量的声明和定义
2 函数的声明和定义
3 类型的声明和定义
继承性是对象之间合作的另一种方式(另两种方式是友元类和对象作成员),派生类继承了基类,一个派生类对象除了可以包含基类对象,这一点和对象作成员类似,派生类还可以继承基类中的成员, 派生类对象可以在类外直接使用继承的基类公有成员。
类既可看作模块又可看作类型,继承的引入使这两种观点更加明显,从模块和类型两个角度来仔细考察继承的意义。
C++从C语言继承过来,但是我们的Bjarne博士更具有先见之明,他为了避免受到C语言的局限性,参考了很多的语言,例如:从Simula继承了类的概念,从Algol68继承了运算符重载、引用以及在任何地方声明变量的能力,从BCPL获得了//注释,从Ada得到了模板、名字空间,从Ada、Clu和ML取来了异常。
C++的内部数据类型遵循隐式类型转换规则。假设某个表达市中使用了一个短整型变量,而编译器根据上下文认为这儿需要是的长整型,则编译器就会根据类型转换规则自动把它转换成长整型,这种隐式转换出现在赋值、参数传递、返回值、初始化和表达式中。我们也可以为类提供相应的转换规则。
对一个类建立隐式转换规则需要构造一个转换函数,该函数作为类的成员,可以把该类的对象和其他数据类型的对象进行相互转换。声明了转换函数,就告诉了编译器,当根据句法判定需要类型转换时,就调用函数。
有两种转换函数。一种是转换构造函数;另一种是成员转换函数。需要采用哪种转换函数取决于转换的方向。
派生类应当向基类的构造函数传递参数
在c++语言程序中,对象之间的相互通信通过调用成员函数实现。
类的成员函数可以访问全部数据成员,类的对象却只能访问公共成员。
cin是C++预定义的标准输入流对象
可以为参数指定默认值,在函数调用时没有指定与形参相对应的实参时就自动使用默认值。默认参数可以简化复杂函数的调用。
默认参数通常在函数名第一次出现在程序中的时候,如在函数原型中,指定默认参数值。指定默认参数的方式从语法上看与变量初始化相似。例如: void myfunc(int x=0,int y=1); 如果一个函数中有多个参数,则默认参数应从右至左逐个定义。
在对函数进行重载时,不允许为其指定缺省参数,同时,对于重载的函数原型间不允许有类型完全相同的参数列表,对于返回类型,在重载时没有特别的要求。
在c++语言中,数据封装要解决的问题是防止不同模块之间数据的非法访问
C++中产生虚基类的作用就是为了解决继承中的二义性问题,通过虚基类继承可以把父基类中相同的函数只编译一次。
友元函数是独立于当前类的外部函数,但它可以访问该类的所有对象的成员;拷贝构造函数是使用已存在的对象初始化正在生成的对象时调用的成员函数。
在C++语言中,所有的函数都是平行的,即在定义函数时是互相独立的,一个函数并不从属于另一个函数,即函数不能嵌套定义,但可以互相调用。但是不能调用main()函数。
派生类的构造函数的成员初始化列中,不能包含基类的子对象初始化;。
要使引用pr代表变量char * p;,则pr应初始化为 char*& pr = p; 。
在C++语言程序中,对象之间的相互通信通过调用成员函数实现。
在已有作用域中创建的作用域就是嵌套作用域;
文字量、常量和需要类型转换的参数都可以传递给const&参数,但不能传递给非const的引用参数。也就是说对非const引用参数不允许做类型转换。
检查字符串s中是否包含字符串t,若包含,则返回并输出t在s中的开始位置(下标值),否则返回-1。请将程序补充完整。
#include<iostream> using namespace std; int main() { int i,j,k; char s[20]="Today is sunday!",t[10]="sun"; for(i=0;s[i]!='\0';i++) { for(j=i,k=0; t[k]!='\0'&&s[j] == t[k];j++,k++); if(t[k]=='\0') { cout<<"t在s中的开始位置下标为:"<<i<<endl; return i; } } return -1; }
字符串字面量在C中具有数组类型char[N],在C++中则为const char[N],在表达式中当发生数组到指针的转换时,对应的等效指针类型分别是char*和const char*,因此,在C中,char *p = “ABCDEF”是合法的,但让人惊奇的是,上述语句在C++中也是合法的!看起来一个pointer to const char指针被赋予了pointer to char指针,似乎违反了C++中指针转换的more cv-qualified原则。其实字符串字面量在C++中存在两种转换,一种转换依据当前上下文环境,另一种遵循数组到指针的转换。
强类型的C++的泛型,除了使用模板,函数重载以外,还有类型方面的泛化,如void、variant、union。
At the language level, C++ represents interfaces by declarations.
Use dynamic_cast where class hierarchy navigation is unavoidable;
Use dynamic_cast to a reference type when failure to find the required class is considered a failure;
Use dynamic_cast to a pointer type when failure to find the required class is considered a valid alternative;
Use unique_ptr or shared_ptr to avoid forgetting to delete objects created using new;
constructor taking a single argument defines a conversion from its argument type.
explicit Vector(int s); // no implicit conversion from int to Vector
mistakes using the basic_string class include
Using an invalidated or uninitialized iterator
Passing an out-of-bounds index
Using an iterator range that really is not a range
Passing an invalid iterator position
Templates offer compile-time "duck typing";
No significant program is written in just a bare programming language. First, a set of libraries is developed. These then form the basis for further work.
In addition to the standard-library components, most implementations offer "graphical user interface" systems (GUIs), Web interfaces, database interfaces, etc.
确保你的编译器支持Regex,如果你的编译器是GCC-4.9.0或者VS2013以下版本,请升级后,再使用。
the equivalent information represented as a pointer plus offsets.
程序库:头文件(库函数原型,由编译器来做类型检查)+库文件(函数实现,由链接器链接,将代码复制到可执行文件中)。
VC++提供了一个名为comment的指令,它可以搭配lib选项给链接器发送一个特定信息,以链接特定的程序库。因此头文件中的代码:
#pragma comment(lib, "mylib")
将告知链接器链接程序库mylib。一般来说,最好使用项目管理工具,比如nmake或者MSBuild,用于确保将正确的程序库链接到项目中。
大部分C运行时库是以如下方式实现的,在一个静态库或者动态链接库中编译函数,然后在头文件中声明函数原型。开发人员在链接器命令行中提供了程序库,通常还将为该程序库引用头文件,以便编译器能够访问函数原型。只要链接器能够识别该程序库,就可以在项目代码中输入其函数原型(将它定义为外部链接,以便告知编译器函数是在其他地方定义的)。这样可以省去将某些大型文件引入到源代码中的麻烦,因为这些文件中很有可能包含大量不会用到的函数原型。
变量声明向编译器保证变量以给定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。
当您使用多个文件且只在其中一个文件中定义变量时(定义变量的文件在程序连接时是可用的),变量声明就显得非常有用。您可以使用 extern 关键字在任何地方声明一个变量。虽然您可以在 C++ 程序中多次声明一个变量,但变量只能在某个文件、函数或代码块中被定义一次。
类型限定符提供了变量的额外信息。包括有const、volatile、restrict。C++ 允许在 char、int 和 double 数据类型前放置修饰符。修饰符用于改变基本类型的含义,所以它更能满足各种情境的需求。
const 类型的对象在程序执行期间不能被修改改变。
volatile 修饰符 volatile 告诉编译器不需要优化volatile声明的变量,让程序可以直接从内存中读取变量。对于一般的变量编译器会对变量进行优化,将内存中的变量值放在寄存器中以加快读写效率。
restrict 由 restrict 修饰的指针是唯一一种访问它所指向的对象的方式。告诉编译器,对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的方式修改该对象的内容。
C++ 存储类说明符:定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。auto、register、static、extern、mutable、thread_local (C++11)、从 C++ 11 开始,auto 关键字不再是 C++ 存储类说明符,且 register 关键字被弃用。
C++ 允许在 char、int 和 double 数据类型前放置修饰符。修饰符用于改变基本类型的含义,所以它更能满足各种情境的需求。下面列出了数据类型修饰符:signed、unsigned、long、short、
存储类说明符 类型限定符 类型修饰符 标识符
specifier delimiter modifier identifier
extern volatile unsigned long int i;
volatile unsigned long int i=0;
函数参数传值、返回值,都会在栈上有一个值的内存空间的副本,而传址(包括引用),内存副本只是一个内存地址的副本;指向函数体栈帧外一段空间的首地址。
调用类型 描述
传值调用 该方法把参数的实际值复制给函数的形式参数。在这种情况下,修改函数内的形式参数对实际参数没有影响。
指针调用 该方法把参数的地址复制给形式参数。在函数内,该地址用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
引用调用 该方法把参数的引用复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。这意味着,修改形式参数会影响实际参数。
传值、传址(引用)、返回值、返回址(引用),值是函数栈帧上的值,址是本函数栈帧以外上的内在空间上的址。
当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。
当返回一个引用时,要注意被引用的对象不能超出作用域。所以返回一个对局部变量的引用是不合法的,但是,可以返回一个对静态变量的引用。
C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。
C++ 中有大量的函数用来操作以 null 结尾的字符串:supports a wide range of functions that manipulate null-terminated strings。
就 C++ 编程而言,C++ 类为数据抽象提供了可能。它们向外界提供了大量用于操作对象数据的公共方法,也就是说,外界实际上并不清楚类的内部实现。
例如,您的程序可以调用 sort() 函数,而不需要知道函数中排序数据所用到的算法。实际上,函数排序的底层实现会因库的版本不同而有所差异,只要接口不变,函数调用就可以照常工作。
cin 对象的附加函数,比如 getline()函数从外部读取一行,ignore() 函数会忽略掉之前读语句留下的多余字符。
为什么基类指针和引用可以指向派生类对象,但是反过来不行?
在内存中,一个基类类型的指针是覆盖N个单位长度的内存空间。
当其指向派生类的时候,由于派生类元素在内存中堆放是:前N个是基类的元素,N之后的是派生类的元素。
于是基类的指针就可以访问到基类也有的元素了,但是此时无法访问到派生类(就是N之后)的元素。
p是基类指针,p指向的是绿色的部分,但可以通过转换把让p指向派生类(实际上p的指向的地址没有变,只是类型变了,指向的空间的大小不一样而已)
派生类的指针却不能指向基类,那样那越界,就是说小的可以变大,大的却不能变小
而基类指针指向派生类对象实例,是通过多态实现的。只能调用其接口,及虚函数。
静态类型:对象在声明时采用的类型,在编译期既已确定;
动态类型:通常是指一个指针或引用目前所指对象的类型,是在运行期决定的;
静态绑定:绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期;
动态绑定:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期;必须搞清楚的一点是:动态绑定只有当我们指针或引用调用虚函数的时候才会发生。
从上面的定义也可以看出,非虚函数一般都是静态绑定,而虚函数都是动态绑定(如此才可实现多态性)。
所以在C++中有两种类型转换static_cast和dynamic_cast的。
另外C++增加了const,所以又有了const_cast。
对于C的转换,有了reinterpret_cast的转换。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
两个整数相除是整除(integer division),小数部分丢失(即截尾,truncated)。
c++编译器只能对操作数的数据类型一致的表达式求值。要保证操作数的数据类型一致,编译器对所选择的操作数进行提升(promotion)操作(也称为隐式类型转换,implicit conversion)。
程序中一个标识符有意义的部分称为其作用域。例如,块中声明局部变量时,其只能在这个块或这个块嵌套的块中引用。一个标识符的4个作用域是函数范围(function scope)、文件范围(file scope)、块范围(block scope)和函数原型范围(function-prototype scope)。后面还要介绍第五个--类范围(class scope)。
派生类总是负责直接基类的构造。
派生类利用初始化列表构造基类的属性。
有多个基类的情况下,派生类对基类的构造顺序取决于继承顺序,先继承的先构造,最后构造自身。析构顺序与此相反。
静态数据成员共享数据的一种机制,比全局变量安全,能将作用域限制在类内;
Invariants are features of a class that don’t change once they’ve been constructed.
By wrapping a dynamic object with a smart pointer, you can rest assured that memory will be cleaned up appropriately as soon as the object is no longer needed.
左值存储在一个内存单元中,右值存储在寄存器中。使用右值引用可以将右值备份到内存单元中。
相当于快速引用一个寄存器的值。
The static_cast reverses a well-defined implicit conversion, such as an integer type to another integer type. The object-to-cast is of some type that the desired-type implicitly converts to. The reason you might need static_cast is that, generally, implicit casts aren’t reversible.
一个字符串从定义到处理再到输出,涉及到编辑器、编译器和输出环境三个因素,正确的处理和显示需要三个因素的共同保障,每一个环节都不能出错。
代码编辑器采用何种编码方式决定了字符串最初的编码,比如编辑器如果采用GBK,那么代码文件中的所有字符都是以GBK编码存储。当编译器处理字符串时,可以通过前缀来判断字符串的编码类型,如果目标编码与原编码不同,则编译器会进行转换,比如C++11中的前缀u8表示目标编码为UTF-8的字符,如果代码文件采用的是GBK,编译器按照UTF-8去解析字符串常量,则可能会出现错误。
UTF-8编码的Unicode数据,C++11还是使用了8bits宽度的char类型数组来表示。
#include <iostream> using namespace std; #ifndef _SIZE_T_DEFINED #ifdef _WIN64 typedef unsigned __int64 size_t; #else typedef _W64 unsigned int size_t; #endif #define _SIZE_T_DEFINED #endif void main() { cout<<sizeof(size_t); }
一个行为类似函数的对象,它可以不需参数,也可以带有若干参数,其功能是获取一个值,或者改变操作的状态。
任何普通的函数和任何重载了调用运算符operator()的类的对象都满足函数对象的特征
程序主要功能是描述数据和处理数据。
数据表现为常量和变量。所有的常量、变量都属于某种数据类型。类型决定了数据的操作方式。
对数据的读写通过内存访问实现。C++提供名访问和地址访问方式。可以通过指针变量间址访问所指对象。
引用是对象的别名。用引用访问对象与名访问方式一样。
关键字const用于约束对象为只读。
运算符表示了系统提供对基本数据的操作。表达式是由常量、变量和运算符连结起来,表达一个计算值的式子。
C++用标准流对象实现数据的输入和输出。输入语句,输出语句和表达式语句可以构成顺序执行的简单程序。
C++构成选择结构的条件语句有if语句和switch语句。
if 语句适用于条件判断比较复杂的分支结构。嵌套if语句采用就近匹配的原则。
用括号{ }改变复合语句结构,可以改变if与else的匹配关系。
switch语句根据一个表达式的不同可能值决定选择执行,适用于条件判断比较简单的多路选择。
循环结构有whlie语句、do_while 语句和 for 语句。
while语句和do_while语句主要用于条件循环。
for语句是C++中很灵活的循环语句,既可以用于控制次数循环,也可以用于条件循环。
转向语句是程序的流程控制的补充机制。C++的转向语句主要有:break、continue和goto语句。
内联函数是C++为降低小程序调用开销的一种机制
函数重载是以同一个名字命名多个函数实现版本
常数据成员是指数据成员在实例化被初始化后约束为只读。
常成员函数是指成员函数的this指针被约束为指向常量的常指针,函数体内不能修改数据成员的值。
构造函数和类型转换函数可以实现基本类型与类类型,以及类类型之间的类型转换。
多态性特别适合于实现分层结构的软件系统,便于对问题抽象时定义共性,实现时定义区别
C++中没有接口的概念
C++中可以使用纯虚函数实现接口
接口类只是一个功能说明,而不是功能实现。
子类需要根据功能说明定义功能实现。
By default, an enum class has only assignment, initialization, and comparisons (e.g., == and <; §1.4) defined.
If you don’t want to explicitly qualify enumerator names and want enumerator values to be ints (without the need for an explicit conversion), you can remove the class from enum class to get a ‘‘plain’’ enum. The enumerators from a ‘‘plain’’ enum are entered into the same scope as the name of their enum and implicitly converts to their integer value.
机器字长:CPU 一次能处理数据的位数,与 CPU 中的 寄存器位数有关
一个字符集就是在字符与整数值之间的一种映射;
basic_string是一个模板类,string是模板形参为char的basic_string模板类的类型定义;
'basic_string
C语言的类型转换问题在于操作比较含糊,而C++的类型转换更容易更醒目;
函数指针模板类
#include <functional> #include <iostream> int foo() { return 5; } int goo() { return 6; } int main() { std::function<int()> fcnPtr = foo; // declare function pointer that returns an int // and takes no parameters fcnPtr = goo; // fcnPtr now points to function goo std::cout << fcnPtr(); // call the function just like normal return 0; }
auto fcnPtr = foo; typedef bool (*validateFcn)(int, int); bool validate(int x, int y, bool (*fcnPtr)(int, int)); // ugly bool validate(int x, int y, validateFcn pfcn) // clean void setID(int id) { m_id = id; }
is converted by the compiler into:
void setID(Simple* const this, int id) { this->m_id = id; }
such as teacher and department class (aggregation), unlike persiona and head class(composition).
associations such as patient and doctor class.
提取方法:将大方法的一部分转换为便于理解的新方法;
提取类:将现有类的部分代码转移到新类中;
在C中,描述库函数的头文件是接口,在面向对象编程中,类的接口是公有属性和方法的集合;
标记(mark)和清扫(sweep)是一种垃圾回收方法。使用这种方法的垃圾回收器定期检查程序中的每个指针,并将指针引用的内存标记为仍在使用。在每一轮周期结束时,未标记的内存视为没有在使用,因而被释放。
1 在垃圾回收器中注册所有指针,这样就可以进行遍历操作;
2 让所有对象都从一个混入类中派生,允许垃圾回收器将对象标记为正在使用;
3 确保垃圾回收器运行时不能修改指针;
#include <iostream> #include <memory> using namespace std; class Simple { public: void go(){} }; void leaky() { Simple* p = new Simple(); p->go(); delete p;//如果go()抛出异常,delete得不到执行从而产生内存泄露 } void notleaky() { unique_ptr<Simple> p(new Simple()); //c+11 p->go(); }
每个普通的方法调用都会传递一个指向对象的指针,这就是称为“ 隐藏”参数的this指针。使用这个指针可访问数据成员或者调用方法,也可将其传递给其他方法或函数。有时还用它来消除名称的歧义。
委托构造函数delegating constructors允许构造函数在初始化列表中调用同一个类的其他构造函数。
对象的移动语义move semantics需要实现移动构造函数move constructor和移动赋值函数运算符move assignment operator。如果源对象是操作结束后被销毁的临时对象,编译器就会使用这两个方法。这两个方法将数据成员从源对象移动到新对象,然后使源对象处于有效但不确定的状态。通常会将源对象的数据成员重置为空值。这样做实际上将内存和其他资源的所有权从一个对象移动到另一个对象。这两个方法基本上只对成员变量进行表层复制(shallow copy),然后转换已分配内存和其他资源的所有权,从而阻止悬挂指针和内存泄漏。
编译器必须在编译期间对constexpr函数求值,函数也不允许有任何副作用。
函数不可以嵌套定义,但可以嵌套lambda表达式;
使用UTF-8时中,一个Unicode字符编码为1-4个char.
In C++14, we can assign binary literals by using the 0b prefix:
void printInteger(const int myValue) { std::cout << myValue; } const int usersAge { age }; // usersAge can not be changed
Runtime constants are those whose initialization values can only be resolved at runtime (when your program is running). Variables such as usersAge and myValue in the snippets above are runtime constants, because the compiler can’t determine their initial values at compile time. usersAge relies on user input (which can only be given at runtime) and myValue depends on the value passed into the function (which is only known at runtime). However, once initialized, the value of these constants can’t be changed.
#include <iostream> int main() { // start outer block int x(5); { // start nested block int y(7); // we can see both x and y from here std::cout << x << " + " << y << " = " << x + y; } // y destroyed here // y can not be used here because it was already destroyed! return 0; } // x is destroyed here
垃圾回收可能也会引发一些问题,如当垃圾回收器运行时,程序可能会停止响应。析构函数具有不确定性。
When you should use namespaces
When you write a library or code that you want to share otherwise, place the code inside a namespace. You can’t know what the environment your code will be used in looks like, which can quickly lead to conflicting names. Placing library code inside a namespace also allows the user to see the contents of your library by using their editor’s auto-complete and suggestion feature.
In applications, namespaces can be used to separate application code and code that could be split off into a library. For example helper functions for strings and localization or math. The application needs those, but they could be replaced by a library or used in another project.
enum class 可以避免枚举常量的命名冲突。
int main() { enum class Color // "enum class" defines this as a scoped enumeration // instead of a standard enumeration { RED, // RED is inside the scope of Color BLUE }; enum class Fruit { BANANA, // BANANA is inside the scope of Fruit APPLE }; Color color = Color::RED; // note: RED is not directly accessible any more, // we have to use Color::RED Fruit fruit = Fruit::BANANA; // note: BANANA is not directly accessible any more, // we have to use Fruit::BANANA if (color == fruit) // compile error here, as the compiler doesn't know // how to compare different types Color and Fruit std::cout << "color and fruit are equal\n"; else std::cout << "color and fruit are not equal\n"; return 0; }
exit(0); // terminate and return 0 to operating system
C++理论上用面相对象的思维构建程序更好。对于一个稍微大型一些的项目,最上层应该是App对象,用于整个程序的启动,视情况看是否需要持续运行而搞定事件循环。先设计好各种系统支撑类,如配置文件相关,日志相关,命令行参数解析相关,多线程管理,异步队列,插件模型等。接下来就是业务功能相关类的抽象,并考虑如何结合到App中初始化并运行。搭积木更适合大型程序的构建方式,即可以优先构造下级模块,也可以先定义接口组装模块再逐步实现下级功能。
It is sometimes the case that we know we want a loop to execute at least once, such as when displaying a menu. To help facilitate this, C++ offers the do-while loop:
// int selection; do { std::cout << "Please make a selection: \n"; std::cout << "1) Addition\n"; std::cout << "2) Subtraction\n"; std::cout << "3) Multiplication\n"; std::cout << "4) Division\n"; std::cin >> selection; } while (selection != 1 && selection != 2 && selection != 3 && selection != 4); // do something with selection here // such as a switch statement
For loops in old code
In older versions of C++, variables defined as part of the init-statement did not get destroyed at the end of the loop. This meant that you could have something like this:
for (int count=0; count < 10; ++count) // count defined here std::cout << count << " "; // count is not destroyed in older compilers std::cout << "\n"; std::cout << "I counted to: " << count << "\n"; // so you can still use it here
This use has been disallowed, but you may still see it in older code.
int (*array)[5] = new int[10][5];
Default parameters won’t work for functions called through function pointers. Default parameters are resolved at compile-time (that is, if you don’t supply an argument for a defaulted parameter, the compiler substitutes one in for you when the code is compiled). However, function pointers are resolved at run-time. Consequently, default parameters can not be resolved when making a function call with a function pointer. You’ll explicitly have to pass in values for any defaulted parameters in this case.
One nuance of C++ that is often missed or misunderstood is that access control works on a per-class basis, not a per-object basis.
经常忽略或误解的C++的一个细微差别是,访问控制在每个类的基础上工作,而不是基于每个对象。
Note that if you use the exit() function, your program will terminate and no destructors will be called. Be wary if you’re relying on your destructors to do necessary cleanup work (e.g. write something to a log file or database before exiting).
##将参数连接到一起。
However, later, the const keyword was added to the language, and l-values were split into two sub-categories: modifiable l-values, which can be changed, and non-modifiable l-values, which are const.
1 文件布局
1.1 遵循统一的布局顺序来书写头文件。
头文件布局:
文件头(参见第三章“注释”)
#ifndef文件名_H(全大写) #define文件名_H 其它条件编译选项 #include(依次为标准库头文件、非标准库头文件) 常量定义 全局宏 全局数据类型 类定义 模板(template)(包括C++中的类模板和函数模板) 全局函数原型 #endif
1.2 遵循统一的布局顺序来书写实现文件
实现文件布局:
文件头(参见第三章“注释”)
#include(依次为标准库头文件、非标准库头文件) 常量定义 文件内部使用的宏 文件内部使用的数据类型 全局变量 本地变量(即静态全局变量) 局部函数原型 类的实现 全局函数 局部函数
遵循统一的顺序书写类的定义及实现
类的定义(在定义文件中)按如下顺序书写:
公有属性 公有函数 保护属性 保护函数 私有属性 私有函数
类的实现(在实现文件中)按如下顺序书写:
构造函数 析构函数 公有函数 保护函数 私有函数
声明类的时候,public、protected、private关键字与分界符{} 对齐,这些部分的内容要进行缩进。
一个变量有且只有一个功能,不能把一个变量用作多种用途。
一条语句只完成一个功能。
断言是用来处理不应该发生的错误情况的,对于可能会发生的且必须处理的情况要写防错程序,而不是断言。如某模块收到其它模块或链路上的消息后,要对消息的合理性进行检查,此过程为正常的错误检查,不能用断言来实现。
指向指针的指针及更多级的指针必须逐级检查。
使用断言检查函数输入参数的有效性、合法性。
对所有具有返回值的接口函数的返回结果进行断言检查。
对象的状态:对象的数据成员,对象的成员函数可以分为:修改状态的成员函数和访问状态的成员函数。
封装有修改全局变量的函数要小心了,因为封装的目的是重用,重用会造成对全局变量的不可控,特别是当全局变量用做数组下标时。
赋值式初始化的限制:
1 如果一个对象需要两个入参呢?,不支持需要多个入参的构造。(其构造函数有两个参数);
2 堆对象的双重初始化:int* p = new int(5);
int a=3;
int b=4;
int c=a+b;
CPU计算a+b,即不能借用a和b的内存,也不能借用赋值目标c的内存,总得有个临时存储位置吧?这位置可能是某块临时内存,也可能是某些个寄存器。
动作数据化的三种方法:
1 函数指针;
2 函数对象;
3 Lambda函数;
工厂函数:一种外部构造函数,称为“工厂式”构造的思路。将构造函数私有化,再通过友元函数以实现在特定场合创建出唯一的并且不允许复制的对象。
Functions can't capture local variables or be defined at local scope; if you need those things, prefer a lambda where possible, and a handwritten function object where not. On the other hand, lambdas and function objects don't overload; if you need to overload, prefer a function (the workarounds to make lambdas overload are ornate). If either will work, prefer writing a function; use the simplest tool necessary.
函数无法使用函数体外部的局部变量,也不能定义在局部作用域;如果你需要这方面功能,如果可能的话使用lambda表达式是较好的选择,否则需要自己实现函数对象。另一方面,lambda表达式和函数对象无法实现重载;如果你需要重载,函数更合适(通过折腾让lambda表达式重载的方法太高级)。如果两种方式都可用,用函数更好;使用满足需要的,最简单的工具。
Operators can be chained together such that the output of one operator can be used as the input for another operator.
local variables have the same definitions for scope and lifetime.
Whitespace is a term that refers to characters that are used for formatting purposes. In C++, this refers primarily to spaces, tabs, and newlines.
By default, non-const variables declared outside of a function are assumed to be external. However, const (and constexpr) variables declared outside of a function are assumed to be internal.
string szU8 = WideChar2narrow(newstr,u8Len);
甲、乙、丙、丁、戊、己、庚、辛、壬、癸
All memory allocated on the stack is known at compile time. Consequently, this memory can be accessed directly through a variable.
指针可以指向一个编译时确定的对象(栈对象),也可以指定一个运行时确定的对象(堆对象)。
内置类做数据成员的类的类型。
The advantage of a functor over a normal function is that functors can store data in member variables (since they are classes).
Binding refers to the process that is used to convert identifiers (such as variable and function names) into addresses.
However, the advantage of late binding is that it is more flexible than early binding, because decisions about what function to call do not need to be made until run time.
A pure virtual function makes it so the base class can not be instantiated, and the derived classes are forced to define these functions before they can be instantiated. This helps ensure the derived classes do not forget to redefine functions that the base class was expecting them to.
An interface class is one with no member variables and all pure virtual functions.
An import library is a library that automates the process of loading and using a dynamic library.
C和C++的缺点就是需要写大量的内存管理代码,这是相对于Java的缺点。
函数对象,是一个对象在调用一个函数,这个函数叫operator()。
封装的概念,将函数定义到结构体内,同时成员具有访问控制,从而实现了信息隐藏。
内部类:只给外部类的成员函数使用,可以定义为内部类为private,这也是一种封装和隐藏。如STL中窗口的迭代器类就是定义在容器类内的。
掌握C++,怎样才算是入了一个门呢?
1 能够自己比较优雅地实现字符串操作函数;
2 实现字符串类;
3 实现智能指针,如shared_ptr;
4 实现迭代器
具有加载和运行功能的监控程序是操作系统的原型。
静态成员函数访问非静态数据成员
class X { public : static void StaFun ( int i , X *ptr ) ; int staDat ; } ; void X :: StaFun ( int i , X * ptr ) { ptr -> staDat = i ; // 正确 }
一个类的构造函数只有一个参数,而且这个参数不是该类的类型而是其他类型,这个构造函数就被称为类型转换构造函数。转换构造函数可以用来处理不同域的同一个数据值。
调用非const类型的引用形参,实参必须不是const类型的,而且实参的类型和形参的类型应当一致。调用一个有const引用的形参的函数时,如果实参不是一个变量或者类型不匹配时,函数会创建一个无名的临时变量用来存储实参的值,并把这个形参作为该临时变量的引用。
编译器帮你做查找替换工作,帮你自动生成代码。
#ifndef __INCvxWorksh #define __INCvxWorksh #ifdef __cplusplus // 编译器按照C语言的方式编译函数名 extern "C" { #endif #ifdef __cplusplus } #endif #endif //__INCvxWorksh
#ifdef __cplusplus cout<<"c++"; #else cout<<"c"; #endif #ifdef __cplusplus printf("c++"); #else printf("c"); #endif
#include <thread>这行出现
fatal error C1083: 无法打开包括文件:“thread”: No such file or directory
这好像是c++11新特性,请问如何在vs2010中使用这种新特性?
2012支持都不完全,更不用说2010了,也就2013支持比较完整一点。
相对运行于虚拟机语言(如C#/Java),C/C++直接以静态形式把源程序编译为目标平台的机器码。一般而言,C/C++程序在编译及链接时可进行的优化最丰富,启动时的速度最快,运行时的额外内存开销最少。而C/C++相对动态语言(如Python/Lua)也减少了运行时的动态类型检测。此外,C/C++的运行行为是确定的,且不会有额外行为(例如C#/Java必然会初始化变量),也不会有如垃圾收集(GC)而造成的不确定性延迟,而且C/C++的数据结构在内存中的布局也是确定的。有时C++的一些功能会使程序性能优于C,当中以内联和模版最为突出,这两项功能使C++标准库的sort()通常比C标准库的qsort()快多倍(C可用宏或人手编码去解决此问题)。另一方面,C/C++能直接映射机器码,之间没有另一层中间语言,因此可以做底层优化,例如使用内部(intrinsic)函数和嵌入汇编语言。然而,许多C++的性能优点并非免费午餐,代价包括较长的编译链接时间和较易出错,因而增加开发时间和成本。
C++中,A–65,a–97,异或表示不考虑进位的加法65^32=97,97^32=65
数据成员分为static、nonStatic,成员函数则还有virtual。数据成员的nonStatic放在一起,跟结构体一样,也有内存对齐。而vitrual的对象都有一个指针指向virtual table。static的数据成员则是独立于对象而存放。
类对象如何逆模(modeling)出数据成员和函数成员呢?
通过对象指针可以引用通过前向引用声明声明的指针。
私有继承:基类的public和protected成员都以private身份出现在派生类中,但基类的private成员不可直接访问。
派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
通过派生类的对象不能直接访问基类中的任何成员。
保护继承:基类的public和protected成员都以protected身份出现在派生类中,但基类的private成员不可直接访问。
派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
通过派生类的对象不能直接访问基类中的任何成员
protected成员的特点和作用:
对建立其所在类对象的模块来说,它与 private 成员的性质相同。
对于其派生类来说,它与 public 成员的性质相同。
既实现了数据隐藏,又方便继承,实现代码重用
继承时的构造函数:
基类的构造函数不被继承,派生类中需要声明自己的构造函数。
声明构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化,自动调用基类构造函数完成。
派生类的构造函数需要给基类的构造函数传递参数
使用类模板使用户可以为类声明一种模式,使得类中的某些数据成员、某些成员函数的参数、某些成员函数的返回值,能取任意类型(包括基本类型的和用户自定义类型)。
赋值符和条件运算的优先级都低于提取符:
常数据成员是指数据成员在实例化被初始化后约束为只读。
常成员函数是指成员函数的this指针被约束为指向常量的常指针,函数体内不能修改数据成员的值。
派生类对基类成员的访问由继承方式和成员性质决定。
为什么通常都将迭代器类设计成容器类的公有内嵌类?这样设计有什么好处?
为了访问容器中的某一具体元素,容器类一般都有一个对应的迭代器类。如果程序用到多种容器,则对应也会用到多个迭代器类。如果将迭代器类设计成独立的一个类,则必须为每个迭代器类取不一样的名字,这样用户程序员必须记住多个容器类的名字和多个迭代器类的名字。但如果将迭代器类设计成容器类内嵌类,这些迭代器类就可以取同样的名字,用户程序员只要记一个类名就可以了。由于将迭代器类设置成容器类的公有内嵌类,用户程序员可以通过“容器类名::迭代器类名”定义相应容器的迭代器对象。
char a='a';
cout<<(int)(a)<<endl;
cout<<static_cast
C++’s basic types fall into two groups. One group consists of values that are stored as integers.The second group consists of values that are stored in floating-point format.The integer types differ from each other in the amount of memory used to store values and in whether they are signed or unsigned. From smallest to largest, the integer types are bool,char, signed char, unsigned char, short, unsigned short, int, unsigned int, long,unsigned long, and, with C++11, long long, and unsigned long long.There is also a wchar_t type whose placement in this sequence of size depends on the implementation.C++11 adds the char16_t and char32_t types, which are wide enough to hold 16-bitand 32-bit character codes, respectively. C++ guarantees that char is large enough to hold any member of the system’s basic character set, wchar_t can hold any member of the system’s extended character set, short is at least 16 bits, int is at least as big as short,and long is at least 32 bits and at least as large as int.The exact sizes depend on the implementation.
Here are some things commonly found in header files:
Functio prototypes
Symbolic constants defined using #define or const
Structure declarations
Class declarations
Template declarations
Inline functions
C++关键的问题在于想用原始的设计完成高级功能,以前看过一个高人总结的,C++再怎么“面向对象”地继承、多态……编译完之后全都没了。编译机制导致C++不可能成为一个合格的面向对象的语言。
禁止对象产生于堆或非堆中
class Demo { public: …… private: static void *operator new(size_t size); static void operator delete(void *ptr); } Demo *pDemo = new Demo(); //error Demo demo; //ok class Demo { public: …… protected: ~Demo(); } Demo *pDemo = new Demo(); //ok Demo demo; //error
算法就是一个或多个函数(确定原型(接口)、输入检查(严进宽出)、边界考虑、出错处理,性能优化)。
一个不能被继承的类
class FinalClass1 { public: static FinalClass1* GetInstance() { return new FinalClass1; } static void DeleteInstance( FinalClass1* pInstance) { delete pInstance; pInstance = 0; } private: FinalClass1() {} ~FinalClass1() {} };
Composition by reference(不持有需要的对象,只持有目标对象的指针)
GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理 C语言。GCC 很快地扩展,变得可处理 C++。后来又扩展能够支持更多编程语言,如Fortran、Pascal、Objective-C、Java、Ada、Go以及各类处理器架构上的汇编语言等,所以改名GNU编译器套件(GNU Compiler Collection)。GNU(GNU's Not Unix!)
那么什么情况下才需要使用extern “C”呢?
1 由于系统内核一般是使用C语言来编写的,系统内核中用C语言实现了很多库。而上层应用程序有可能是用C++来开发,如果在内核库函数头文件中不用extern “C”来声明库函数的话,在编写C++应用程序时,包含库头文件,在C++文件链接时就会以C++标准来链接库的函数名,而在库文件实现时是用C来实现的(C编译器编译的),二者函数名不同,在链接时就会出现找不到函数的现象。
2 在有些工程中,既包含.c文件又包含.cpp文件,如VC的工程。.cpp文件要调用.c文件中的函数时,需要extern “C” 声明.c文件中的函数,在CPP文件中让C++编译器使用C的标准来链接C文件中的函数
为什么空类实例化会有一个字节大小?因为需要一个地址,一个字节会对应一个地址。
而如果是没有数据成员的虚函数,则是4个字节。
封装:封装一个类;
继承:定义一个子类,如利用MFC库中的一个类自定义一个子类;
多态:重写虚函数,如重写MFC库中的类的虚函数;
C语言用返回值为处理异常,不明确。
c++在try中throw(可以包含在函数中)异常,后有类型,然后按类型catch捕捉。
如果使用函数指针,则可以实现其行为的泛型。
引用是变量的别名,函数返回的引用,则函数调用就是变量的别名。
产生一定范围随机数的通用表示公式
要取得 [a,b) 的随机整数,使用 (rand() % (b-a))+ a;
要取得 [a,b] 的随机整数,使用 (rand() % (b-a+1))+ a;
要取得 (a,b] 的随机整数,使用 (rand() % (b-a))+ a + 1;
通用公式: a + rand() % n;其中的 a 是起始值,n 是整数的范围。
要取得 a 到 b 之间的随机整数,另一种表示:a + (int)b * rand() / (RAND_MAX + 1)。
要取得 0~1 之间的浮点数,可以使用 rand() / double(RAND_MAX)。
成员函数是类与外部程序的接口。
C++的RAII(resource acquisition is initialization,资源获取就是初始化)比较独特,C/C#/Java没有相应功能。
根据RAII对资源的所有权可分为常性类型和变性类型,代表者分别是boost::shared_ptr<>和std::auto_ptr<> ;从所管资源的初始化位置上可分为外部初始化类型和内部初始化类型。
常性类型是指获取资源的地点是构造函数,释放点是析构函数,并且在这两点之间的一段时间里,任何对该RAII类型实例的操纵都不应该从它手里夺走资源的所有权。
变性类型是指可以中途被设置为接管另一个资源,或者干脆被置为不拥有任何资源。
外部初始化类型是指资源在外部被创建,并被传给RAII实例的构造函数,后者进而接管了其所有权。boost::shared_ptr<>和std::auto_ptr<>都是此类型。与之相对的是内部初始化类型。
其中,常性且内部初始化的类型是最为纯粹的RAII形式,最容易理解,最容易编码。
相对于运行于虚拟机的语言(如C#/Java),c/c++直接以静态形式把源程序编译为目标平台的机器码。一般而言,
c/c++程序在编译及链接时可进行的优化最丰富,启动时的速度最快,运行时的额外内存开销最少。而c/c++相对动态语言(如Python、Lua)也减少了运行时的动态类型检测。此外,c/c++的运行行为是确定的,且不会有额外行为(例如C#/Java必然会初始化变量),也不会有如垃圾收集(GC)而造成的不确定性延迟,而且c/c++的数据结构在内存中的布局也是确定的。有时C++的一些功能会使程序性能优于C,当中以内联和模版最为突出,这两项功能使C++标准库的sort()通常比C标准库的qsort()快多倍(C可用宏或人手编译去解决此问题)。另外一方面,c/c++能直接映射机器,之间没有另一层中间语言。
The keyword this indicates a pointer to the current object.
在C语言中,赋值表达式并非左值,因而“(a=3*5)=4*3”在语法上就是错误的,甚至根本无法编译;然而在C++语言中,赋值表达式却是左值。由于误把C程序作为C++程序编译,就会产生“(a=3*5)=4*3”是合法的C语言表达式那样的误解。
const int n=5; int *p = (int *)&n; ++*p ;
在C++语言中,++运算和*运算具有相同的优先级,且运算结合的方向是自右向左,所 以*p++和*(p++)的作用是完全相同的,即首先得到p所指向的内容,再将p向下移动一位(指 向下一个元素)。但是与*(++p)的作用就不相同了,*(++p)是先对指针p加1,再取*p。
编译器把调用成员函数的对象的地址赋给 this指针
malloc是库函数,不在编译器控制范围之内;new是运算符,在编译器控制范围之内。
调用malloc时,从堆中申请内存;调用new时,从堆中申请内存并为内存调用构造函数。