完整表达式是指该表达式不是另一个更大表达式的子表达式。
对于y = (4 + x++) + (6 + x++);
c++并不能保证每个子表达式计算完后x的值+1,只能保证整个表达式计算完后x+2。所以要避免这种情况。
>,< 的结合方向从左往右。
++a
与后缀格式a++
对于内置类型,两者执行速度一样。
对于类(用户设置的类型),前缀更快。
后缀比前缀优先级高,前缀优先级和解除引用运算符的优先级一样高。
(优先级一样高时从右往左)
优先级最低
从第一个表达式开始计算,再到第二个…
逗号表达式的值为最后一个表达式的值。
#include
int main(int argc, char** argv) {
using namespace std;
int array[]={1,2,3,4,5,6,7,6,5,4,3,2,1};
for(int i:array)//如果想修改数组,可把i设为引用for(int &i:array)
{
cout<<i<<endl;
}
return 0;
}
#include
#include
#include
#include
int main(){
using namespace std;
ifstream readFile;
ofstream writeFile;
enum doWhat{readfile,writefile,exitProgram
};
int m;
cout<<"Read the file input 0,write the file input 1,finish the program input 2\n";
cin>>m;
while(1){
if(m==readfile){
cout<<"readFile : Please input the name of file\n";
char fileName[20];
cin>>fileName;
readFile.open(fileName);
if(!readFile.is_open()){
cout<<"Could not open the file"<<fileName<<endl;
cout<<"Program terminating.\n";
exit(EXIT_FAILURE);
}
char ch;
/*string theText;
readFile.getline>>theText;*/
cout<<"The text of file is : "<<endl;
while(readFile.get(ch))
cout<<ch;
if(readFile.eof()) cout<<"\nArrived the end of file\n";
else if(readFile.fail()) {
}
else if(readFile.bad()) cout<<"\nFile is bad\n";
readFile.close();
}else if(m==writefile){
cout<<"writeFile : Please input the name of file\n";
char fileName[20];
cin>>fileName;
writeFile.open(fileName);
cout<<"Please write the text you want input to file:\n"<<
fileName<<":\n";
char theText[100];
cin.get();//空行处理
cin.clear();
cin.get(theText,100);
writeFile<<theText;
writeFile.close();
}else if(m==exitProgram) break;
else cout<<"Please write 1 or 0 or 2 ! \n";
cout<<"Read the file input 0,open the file input 1,finish the program input 2\n";
cin>>m;
}
cout<<"OK Please write anything and Enter to exit ! ";
cin.clear();
cin.get();
cin.get();
return 0;
}
return_type function(...);
const int*pt 与 int* const pt
;const int*pt
表示指向常量。即不能通过该指针修改所指向内存的值。
int* const pt
表示常量指针。即不能修改该指针的值(不能再指向其他地方了)。
和声明函数一样,只是把函数名用(*pf)
代替:
int (*pf)(double x,int y);//声明了一个参数(double x,int y),返回值类型为int 的函数指针。
int (*p1)(double x,int y) = f1;//初始化
auto p1 = f2;//auto 自动类型推断 下面细说
y = (*pf)(3.14,6);
//c++还允许:
y = pf(3.14,6);
const double* (*pa[3])(const double *,int)={f1,f2,f3};
运算符[]的优先级高于 * ,因此*pa[3]
表示pa是一个包含3个指针的数组。
不能用 auto 自动类型判断,因为 auto 只适用于单值初始化。
指向函数指针数组的指针呢:
方法一:
auto pc = &pa;
方法二:
const double *(*(*pd)[3])(const double *,int) = &pa;
注意:
*pp[3] —— 包含三个指针元素的数组
(*pp)[3] —— 包含三个元素的数组,pp是指向该数组的指针
*(*pp)[3] —— 包含三个指针元素的数组,pp是指向该数组的指针
通过指向函数指针数组的指针进行函数调用:
(*pd)[i](p,6);
或: (*(*pd)[i])(p,6);
调用后返回指针所指的内存的值:
*(*pd)[i](p,6);
或:*(*(*pd)[i])(p,6);
int (*pf)(double x,int y);//声明了一个参数(double x,int y),返回值类型为int 的函数指针。
typedef int (*p_func)(double x,int y);//声明了一个参数(double x,int y),返回值类型为int 的函数指针类型的别名:p_func
p_func pa[3] = {f1,f2,f3};
确保变量的类型与赋给它的初值的类型一致。即使你赋的初值类型不对。(见笔记一,基础与数据)
省去函数调用的处理时间,因而速度较快。
一般用于代码较短,本身处理就很快的函数。
inline int square(int x);
...
inlint int square (int x)
{
return x*x;
}
或直接:
inlint int square (int x){return x*x;}
注意:
int i = 12;
int & n =i;
引用变量 n 将是变量i 的别名,其值与地址都是一样的。
注意:
对于既能用引用传递也能按值传递的函数,若参数较大(内存较大的对象或结构)时,用 const 引用参数更快些,否则就按值传递。
若引用参数是 const
时,在以下两种情况下会创建临时变量。
左值:是可被引用的数据对象,现在 变量和const变量都可视为左值。
非左值:包括字面常量,多项式等
一旦创建临时变量就和按值传递很像了,不会影响实参,是为了防止函数改变实参(因为引用参数是const的,本意就是为了防止改变实参)
不可通过引用修改被引用对象的值,无论被引用对象是不是const 常量
const 引用可绑定到非常量对象,字面值常量,一般表达式,或者可自动转换的其他类型变量
double v =3.14;
int i=2;
const int &r0 = 3;
const int &r1 = v;
const int &r2 = i+1;
//创建值为3的临时int量让 r1,r2 绑定
//所以只能是const引用
理由有三:
——尽可能将引用形参声明为const
改程序只是演示了其本质,正确用法看第二个程序
#include"iostream"
using namespace std;
int & is0 (int& num);
int main()
{
int i=12;
cout<<"i : "<<i<<endl;
is0(i)++;
cout<<"i : "<<i<<endl;
getchar();
return 0;
}
int & is0(int& num)
{
if(num==0)
cout<<num<<"is 0 \n";
else
cout<<num<<"isn't 0\n";
return num;
}
结果:
i : 12
12isn't 0
i : 13
返回的仍是返回值所引用的变量。是左值(被引用的变量不是函数的临时变量)。
常规类型的返回是右值,因为它在临时内存中。
#include"iostream"
using namespace std;
const int & is0 (const int& num);
int main(int argc,char ** argv)
{
int i=12,n;
cout<<"i : "<<i<<endl;
n=is0(i);
cout<<"n : "<<i<<endl;
return 0;
}
const int & is0 (const int& num)
{
if(num==0)
cout<<num<<"is 0 \n";
else
cout<<num<<"isn't 0\n";
return num;
}
结果:
i : 12
12isn't 0
n : 12
优点:一般按值传递的返回,其实 是创建了临时的变量,而直接返回引用省略了这一步骤,更高效。
注意:
可引用右值
一般用于库设计人员能够提供有些操作的更有效实现。
int func(int x,int y=0);
关键——函数特征标(参数列表)——参数类型,参数个数
这些不同就可以重载
template <typename T>
void MySwap(T x,T y);
...
template <typename T>
void MySwap(T x,T y)
{
T t;
t=x;
x=y;
y=t;
}
emplate
ADD(T1 x,T2 y);
template<typename T>
void Swap(T &a, T &b);
template<typename T>
void Swap(T *a,T *b,int n);//void Swap(T a[],T b[],int n);
将模板函数的某些类型具体化,再定义。优先于一般模板。
第三代具体化:
template<>
打头struct job
{
char name[40];
double salary;
int floor;
}
...
template<> void Swap<job>(job&,job&);
//Simple Form:
template<> void Swap(job&,job&);
template void Swap<int>(int,int);
或者在程序中使用函数来创建显式实例化
在需要类型自动转换时可以指定转换成哪种函数
//或者在语句中使用:
template <class T>
T Add(T a,T b)
{
return a+b;
}
...
int m=6;
double x = 12.2;
cout<<Add<double>(x,m)<<emdl;
int& 不能转换成double &或double
下面是错误的
int m = 5;
double x = 14.3;
↓↓↓↓↓ Error ↓↓↓↓↓
Swap<double>(m,x);
↑↑↑↑↑ Error ↑↑↑↑↑
对于单个参数,从最佳到最差的顺序如下:
…
具体规则:P289 ~ P294
C++11 新关键字—— decltype
int x;
decltype(x) y;// y 和 x有相同的类型
可用于函数模板中自动判断返回类型
template<class T1,class T2>
void ft(T1 x,T2 y)
{
...
decltype(x+y) xpy = x +y;
...
}
对于decltype(expression) var
的规则:
auto ADD(T1 x,T2 y) -> decltype((x+y));
...
template <typename T1,typename T2>
auto ADD(T1 x,T2 y) -> decltype((x+y))
{
return (x+y);
}
extern(可选)
,不初始化则默认为0,但可以多次声明(extern)thread_local
可与 static 和 extern 结合使用,指出变量的持续性与其所属线程的持续性相同(就像静态变量与程序的关系)。
multable
指出:即使结构(或类)变量为const,其某个成员也可以被修改
volatile
:表明,即使程序代码没有对内存单元进行修改,其值也可能发生改变P317(一编译器会优化一些变量的处理,即使硬件上内存单元发生了改变,有时也不会被程序及时察觉,使用该关键词可避免该情况)
const
:对于const声明的常量,其链接性为内部的。这意味着可以在头文件中声明这种常量而不用担心重复定义。(c++中)
若希望某const常量的链接性为外部的,可以用 extern 覆盖其内部链接性。这时也需要在其他使用常量的文件中用 extern,但是此时该const常量只能初始化一次(就像一般的外部变量)
//file1.cpp
extern const int bufSize = fcn();
//file1.h
extern const int bufSize;
其作用域就像一般变量一样,对于同名const常量,局部可以隐藏外部
一般默认为外部
c语言与C++的名称修饰不同,需要对C的预编译进行处理,详情见P319。
单值初始化
int *p = new int(6);
int *pi = new int{6};
多值(结构,数组)初始化(c++11 列表初始化)
int *p = new int[4]{1,2,3,4};
struct where{double x,double y,double z};
...
where *pw = new where{1.2,3.6,4.2};
需包含头文件 new
#include
...
char * buffer1[40];
...
double *p = new (buffer1)double (12.88);
...
delete p;
using namespace std; //using编译指令
using std::cout;//using声明指令
尽量使用 using声明 而不用 using 编译指令,有重名时用using声明会报错,提示二义性,而using 编译指令将覆盖。
#include
using namespace std;
namespace Jill
{
int iApple=2;
double dPencil;
}
int main()
{
//using Jill::iApple;//声明名称空间会与同名变量冲突,错误;
using namespace std;//using编译指令,不会与同名变量冲突,而是被局部的同名变量覆盖,但仍可用作用域解析符。
int iApple;
iApple=1;
cout<<Jill::iApple<<endl;//用作用域解析符使用名称空间中的变量
cout<<iApple;
return 0;
}
结果:
2
1
namespace elements
{
namespace fire
{
int flame;
...
}
float water;
}
...
使用 fire内部名称空间:
using namespace elements::fire;
namespace myth
{
using Jill::iApple;
using namespace elements;
using std::cout;
using std::cin;
}
...
访问 iApple:
cout<<myth::iApple;
或
cout<<Jill::iApple;
using namespace myth;
相当于 using namespace myth;
+ using namespace Jill;
namespace MT = myth;
//用于简化:
namespace FE = elements::fire;
using FE::flame;
namespace
{
int count;
}
提供了链接性为内部的静态变量的替代品
放在文件中效果为 static int count;