目录
//通过以下代码可以直接建立起完整的C++知识框架
//俺觉得计算机是个技能型学科,知识点什么的在敲代码的过程中学习就好了
1.动态初始化
//动态初始化
#include<iostream>
using namespace std;
int main(){ //跟C语言不同,想使用的时候才定义变量!!!
double radius=4.0,height=5.0;
cout<<"Enter the height"<<endl;
cin>>height;
double volume=3.1416*radius*radius*height; //动态初始化
cout<<"Volume is "<<volume<<endl;
return 0;
} //目的是为了配合类的构造函数
2.数据类型
//数据类型
#include<iostream>
using namespace std;
int main(){
cout<<"整型:short int、int、long int"<<endl;
cout<<"实型:float、double、long double"<<endl;
cout<<"bool型:bool"<<endl;
cout<<"字符型:char"<<endl;
cout<<"空类型:void"<<endl;
cout<<"枚举型:enum"<<endl;
cout<<"联合(共用体):union"<<endl;
cout<<"结构:struct"<<endl;
cout<<"类:class"<<endl;
cout<<"指针:*"<<endl;
cout<<"引用:&"<<endl;
cout<<"数组:[]"<<endl;
cout<<"可以加signed,unsigned修饰符"<<endl
return 0;
}
3.bool型
//bool类型
#include<iostream>
using namespace std;
int main(){
bool b=false;
cout<<b<<endl;
b=7; //等价于b=true
cout<<b<<endl;
b=3==4; //给b输入的是3是否等于4的结果,若正确b=1,错误则b=0
cout<<b<<endl; //输出的是0(false)或1(true),是数字不是true & false!!!
return 0;
}
4.无名参数
//无名参数
//作用:1.用于以后扩展 2.同于函数重载
#include<iostream>
using namespace std;
void func(int x,int); //第二个参数无名,(x可省略)函数原型声明,C和C++都允许省略形参名
int main(){
func(11,88)
return 0;
}
void func(int x,int){ //C++允许略写参数名,C不能省略(尽管没有用到这个参数)
cout<<x<<endl;
}
5.指针类型转换
//指针类型转换
#include <iostream>
using namespace std;
int main() {
int i=10,*pi;
double d=double(i); //显式类型转换
//等价于double d=(double)i;
cout<<d<<endl;
void *pv=&i; //正确
pi=(int *)pv; //正确
//pi=static_cast<int *>(pv); //正确
//pi=reinterpret_cast<int *>(pv); //正确
//pi=pv; //错误
cout <<*pi<<endl;
//cout <<*pv<<endl; //错误
}
6.循环体内声明循环控制变量
//循环体内声明循环控制变量
#include<iostream>
using namespace std;
int main(){
int sum=0,fact=1; //factorial阶乘
for(int i=1;i<=5;i++){
sum+=i;
fact*=i; //i is known throught the loop
}
//but i is not known here
cout<<"Sum is:"<<sum<<endl;
cout<<"Factorial is:"<<fact<<endl;
return 0;
}
7.复合语句中定义变量
//复合语句中定义变量
#include<iostream>
using namespace std;
void func(int a);
int main(){
func(10);
return 0;
}
void func(int a){
cout<<a<<endl; //输出10
if(a==10){
int a=5,i=20;
cout<<a<<i<<endl; //输出520
}
//cout<<i<<endl; (错误,i无定义)
}
8.使用"::变量名"可以访问具有文件作用域的变量
//使用"::变量名"可以访问具有文件作用域的变量
#include<iostream>
using namespace std;
int hour,minute; //定义全局变量
void incTime(int hour,int minute);
int main(){
int hour=23,minute=58; //定义局部变量
incTime(::hour,::minute); //访问全局变量
incTime(hour,minute); //访问局部变量
incTime(::hour,::minute); //访问全局变量
return 0;
}
void incTime(int hour,int minute){
minute++; //形式参数
if(minute>=60){
minute=0;
hour++; //形式参数
if(hour>=24) hour=0;
}
::hour=hour; //使全局变量等于局部变量
::minute=minute;
cout<<"The time is "<<hour<<":"<<minute<<endl;
}
9.变量作用域的演示实例
//变量作用域的演示实例
#include<iostream>
using namespace std;
int x=1;
void func();
int main(){
cout<<"全局变量"<<x<<endl;
cout<<"通过::限定符访问全局变量"<<::x<<endl;
func();
return 0;
}
void func(){
int x=2; //局部变量,隐藏了全局变量
cout<<"局部变量"<<x<<endl;
{
int x=3; //复合语句中定义的局部变量
//它隐藏了局部和全局变量
cout<<"复合语句中定义的局部变量"<<x<<endl;
cout<<"可以通过::限定符访问全局变量"<<::x<<endl;
cout<<"但不能访问被隐藏的局部变量"<<endl;
}
cout<<"复合语句结束后又可以访问局部变量"<<x<<endl;
cout<<"当然还是可以通过::限定符访问全局变量"<<::x<<endl;
}
/*******************************************
程序输出结果如下:
全局变量1
通过::限定符访问全局变量1
局部变量2
复合语句中定义的局部变量3
可以通过::限定符访问全局变量1
但不能访问被隐藏的局部变量
复合语句结束后又可以访问局部变量2
的人还是可以通过::限定符访问全局变量1
********************************************/
10.数组边界核查
//数组边界核查
//C++和C语言都不会对数组边界进行核查
#include<iostream>
using namespace std;
int main(){
int i=0,crash[10];
for(;i<100;i++){
crash[i]=i; //执行上述程序片段将造成不可预料的结果,并且错误难于定位
}
/* for(int j=0;j<100;j++){ //测试
cout<<crash[j]<<endl;
} */
return 0;
}
11.使用cin.getline从键盘读入字符串
//从键盘读入字符串
//cin不能输入空白符,遇到空白符停止(在字符数组中可能输入会超过数组边界-遇到空白符才停止)
/*cin.getline:第一个参数是要存储字符串的数组的名称。第二个参数是数组的大小。
当 cin.getline 语句执行时,cin读取的字符数将比该数字少一个。为null终止符留出空间。 */
#include<iostream>
using namespace std;
int main(){
char s[80];
cout<<"Enter a string"<<endl;
//cin>>s; //read string from keyboard
//应改为: (否则遇空白停止,可能输入超过80个字符)
cin.getline(s,80); //最多输入79个字符,null 终止符将自动放在数组最后一个字符的后面。
cout<<"Here is your string: "<<s<<endl;
return 0;
}
12.const修饰符
/* const修饰符在C++中很常用(const修饰符是不变的意思)
在C语言中,下述语句是错误的:
const int N=10;
//必须改为 #define N 10
int a[N];
但在C++中,上述语句是允许的 */
/*
!!!返回min, &min, const &min 的区别!!!
1.返回min(对象)
返回的是值,只能出现在赋值符号的右边,效率较低,将欲返回的对象用"拷贝构造函数"
拷贝给调用者,然后调用"析构函数" 销毁这个临时对象。
2. 返回&min(对象引用)
返回的是引用,可以出现在赋值符号的左边(当然可以出现在赋值符号的右边),
将欲返回的对象引用(实际上就是地址)直接赋值给调用者,效率较高。
3.返回const &min(常对象引用)
返回的是常引用,只能出现在赋值符号的右边,将欲返回的对象引用(实际上就是地址)
直接赋值给调用者,效率较高。对自定义的类一般用这种方式返回,常常用来取代第一种方式
*/
/*
1、const double PI=3.14;
//常量定义,默认是静态的(仅用于本文件),比用#define PI 3.14好(有类型便于检查,可计算初值表达式)!
2、int strcmp(const char *s1,const char *s2); //函数不允许修改s1和s2指向的2个字符串!
3、const int &min(int &x,int &y); //表示不能将函数的返回值做为左值使用
例:min(a,b)=5; //错误 (无const时允许做左值)
*/
//无const时允许做左值:
#include<iostream>
int &min(int &a,int &b);
using namespace std;
int main(){
int a=10,b=20,c=30;
cout<<"a="<<a<<" b="<<b<<" c="<<c<<endl;
min(a,b)=5; //函数结果做左值
cout<<"a="<<a<<" b="<<b<<" c="<<c<<endl;
min(b,c)=5;
cout<<"a="<<a<<" b="<<b<<" c="<<c<<endl;
return 0;
}
int &min(int &a,int &b){
return (a<b?a:b);
}
/*
const修饰符定义"只读函数"(使用是一种好习惯,不用不会导致错误):
例如: int getMember() const; 表示该函数是一个"只读函数",
即函数不能改变对象的成员变量的值;也不能调用一个非const的成员函数(否则就可能通过它间接修改数据成员)。
在函数原型和定义处均必须加上const。
拓:
1.非成员函数不能有CV限定(友元函数不是类的成员函数所以它不能用CV限定)。
2.静态成员函数不能有CV限定。*/
//const 定义的常变量不能修改
#include <iostream>
int main() {
const char c1=std::cin.get();
const char c2=c1-32;
//c1=‘a’会报错!
std::cout<<c1<<' '<<c2<<'\n';
return 0;
}
const与指针
/* int i=1,k=2,*p; //p正常指针
p=&i;*p=88;p=&k;//这些语句都正确
但指针p不能指向const变量ci!!! (const int *cp才可以)
const int ci=2; //ci是常量
p=&ci; //'=':cannot convert from 'const int *' to 'int *'
如果允许这样赋值的话,则将可以用形如*p=value;来修改ci的值,显然有问题!
1.const int *cp 指向常量的指针
const int *cp; //等价于:int const *cp;可认为是指向常量的指针,不能通过该类指针来修改所指向变量的值!
cp=&ci; //很明显应该是允许的赋值
cp=&i; //cp可以指向其他普通变量,但无法用*cp=value;来修改i的值!
*cp=value; //assignment of read-only location '*cp'
i=value; //是正常赋值,当然是允许的!
2.int* const pc 指针常量
int* const pc; //错误,必须要预设指针指向哪
int* const pc=&i; //指针常量,即该指针不能被修改(不能指向其他地址)。
*pc=8; //指向的内容是可改的
pc=&k; //l-value specifies const object,但指针本身不可改,很少用!
int * const pc=&ci; //cannot convert from 'const int *' to 'int
3.const int* const cpc 指向常量的指针常量
const int * const cpc1=&ci;
const int * const cpc2=&i;
*cpc1=value; //l-value specifies const object
cpc1=&i; //l-value specifies const object
*/
//演示const修饰符的完整程序
#include <iostream>
int main() {
const int value=8;
const int ci=2;
int i=0,k=1;
std::cout<<"i="<<i<<" ci="<<ci<<std::endl;
std::cout<<"int *\n";
int *p; //正常指针
p=&i;
std::cout<<"*p="<<*p<<" i="<<i<<std::endl;
//可以用形如*p=value;来修改i的值
*p=value;
std::cout<<"*p="<<*p<<" i="<<i<<std::endl;
*p=0; //将i置0
//p也可以指向其他地址
p=&k;
std::cout<<"*p="<<*p<<" k="<<k<<std::endl;
//但p不能指向const变量!如果允许的话,则将可以用形如*p=value;来修改ci的值!
//p=&ci; // '=' : cannot convert from 'const int *' to 'int *'
std::cout<<"const int *\n";
const int * cp; //等价于:int const *cp;可认为是指向常量的指针
//不能通过该类指针来修改所指向变量的值!
cp=&ci; //很明显应该是允许的赋值
std::cout<<"*cp="<<*cp<<" ci="<<ci<<std::endl;
cp=&i; //也允许,但无法用*cp=value;来修改i的值!
std::cout<<"*cp="<<*cp<<" i="<<i<<std::endl;
//*cp=value; //assignment of read-only location '*cp'
//i=value;仍然是正常的赋值,当然是允许的。
i=value;
std::cout<<"*cp="<<*cp<<" i="<<i<<std::endl;
i=0;
cp=&k;
std::cout<<"*cp="<<*cp<<" k="<<k<<std::endl;
//*cp=value; //assignment of read-only location '*cp'
std::cout<<"int * const\n";
//int * const pc; //const object must be initialized if not extern
int * const pc=&i; //指针常量,即该指针不能被修改(不能指向其他地址)
*pc=8;
std::cout<<"*pc="<<*pc<<" i="<<i<<std::endl;
*pc=0; //将i置0
//pc=&k; //assignment of read-only location '*cp'
//int * const pc=&ci; //cannot convert from 'const int *' to 'int *const'
std::cout<<"const int * const\n";
const int * const cpc1=&ci;
std::cout<<"*cpc1="<<*cpc1<<" ci="<<ci<<std::endl;
//*cpc1=value; //assignment of read-only location '*cp'
//cpc1=&i; //assignment of read-only location '*cp'
const int * const cpc2=&i;
std::cout<<"*cpc2="<<*cpc2<<" i="<<i<<std::endl;
}
13.C++其它常见修饰符
1.auto
/* auto
1.声明变量时根据初始化表达式自动推断该变量的类型
auto f=1.414; //double
auto s("LOVE"); //const char*
auto z=new auto(10); // int*
auto x1=6, x2=6.0, x3='a';//错误,必须是初始化为同一类型
2.声明函数时函数返回值的占位符。 */
2.volatile
/* volatile
强制访存操作,防止编译器优化(不进行优化时,自动变量与寄存器变量存放在存储器中;
优化后它们在寄存器中),告诉编译器每次必须去内存中取值,而不是从寄存器或者缓存。
(告诉编译器不需要优化volatile声明的变量,让程序可以直接从内存中读取变量。
对于一般的变量编译器会对变量进行优化,将内存中的变量值放在寄存器中以加快读写效率。)
如果编写一个使用非局部跳转的可移植程序,则必须使用volatile属性。 */
3.register
/*register
用于定义存储在寄存器中而不是 RAM(主存) 中的局部变量。
这意味着变量的最大尺寸等于寄存器的大小(通常是一个词),
且不能对它应用一元的 '&' 运算符(因为它没有内存位置)。
{
register int miles;
}
寄存器只用于需要快速访问的变量,比如计数器。
还应注意的是,定义 'register' 并不意味着变量将被存储在寄存器中,
它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制。 */
4.static(声明静态成员变量/函数)
/* static
局部变量: 指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。
因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。
全局变量:当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。
在 C++ 中,当 static 用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。*/
#include<iostream>
static int count=10;
void func();
using namespace std;
int main(){
while(count--){
func();
}
return 0;
}
void func(){
static int i=0; //若没有static则每次输出的i值都为1
i++;
cout<<"i:"<<i;
cout<<" count:"<<count<<endl;
}
/*************
当上面的代码被编译和执行时,它会产生下列结果:
i:1 count:9
i:2 count:8
i:3 count:7
i:4 count:6
i:5 count:5
i:6 count:4
i:7 count:3
i:8 count:2
i:9 count:1
i:10 count:0
**************/
5.extern
/* extern
用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。
当您使用 'extern' 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。
当您有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,
可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。
可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。
extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候,如下所示:
第一个文件:main.cpp
#include <iostream>
int count ;
extern void write_extern();
int main()
{
count = 5;
write_extern();
}
第二个文件:support.cpp
#include <iostream>
extern int count;
void write_extern(void)
{
std::cout << "Count is " << count << std::endl;
}
//在这里,第二个文件中的 extern 关键字用于声明已经在第一个文件 main.cpp 中定义的 count。
*/
6.mutable
/* mutable
在C++中,mutable 是为了突破 const 的限制而设置的。可以用来修饰一个类的成员变量。
被 mutable 修饰的变量,将永远处于可变的状态,即使是 const 函数中也可以改变这个变量的值。
比如下面这个例子:
#include<iostream>
using namespace std;
class Test{
public:
Test();
int value() const;
private:
mutable int v;
};
Test::Test(){
v=520;
}
int Test::value() const{
v++;
return v;
}
int main(){
Test A;
cout<<A.value()<<endl;
return 0;
}
甚至于当 A 这个变量被声明为 const 类型时 A.value 还是可以改变的。比如下面的代码:
int main(){
const Tset A;
cout<<A.value<<endl;
return 0;
}
*/
7.thread_local
/* thread_local
使用 thread_local 说明符声明的变量仅可在它在其上创建的线程上访问。
变量在创建线程时创建,并在销毁线程时销毁。 每个线程都有其自己的变量副本。
thread_local 说明符可以与 static 或 extern 合并。
thread_local 仅应用于数据声明和定义,thread_local 不能用于函数声明或定义。
以下演示了可以被声明为 thread_local 的变量:
using namespace std;
thread_local int x; // 命名空间下的全局变量
class X
{
static thread_local string s; // 类的static成员变量
};
static thread_local string X::s; // X::s 是需要定义的
void foo()
{
thread_local vector<int> v; // 本地变量
} */
14.引用(&)和指针
实现两个变量的交换
1.用值调用的方式(无法实现,函数无法更改a,b所在地址的值)
#include<iostream>
using namespace std;
void swap(int a,int b);
int main(){
int a=1,b=2;
cout<<"a:"<<a<<" b:"<<b<<endl;
swap(a,b);
cout<<"a:"<<a<<" b:"<<b<<endl;
return 0;
}
void swap(int a,int b){
int temp;
temp=a;
a=b;
b=temp;
}
2.用指针方式
#include<iostream>
void swap(int *a,int *b);
using namespace std;
int main(){
int a=1,b=2;
cout<<"a:"<<a<<" b:"<<b<<endl;
swap(&a,&b);
cout<<"a:"<<a<<" b:"<<b<<endl;
return 0;
}
void swap(int *a,int *b){
int temp;
temp=*a;
*a=*b;
*b=temp;
}
3.用引用调用方式实现
#include<iostream>
using namespace std;
void swap(int &a,int &b);
int main(){
int a=1,b=2;
cout<<"a:"<<a<<" b:"<<b<<endl;
swap(a,b);
cout<<"a:"<<a<<" b:"<<b<<endl;
return 0;
}
void swap(int &a,int &b){
int temp;
temp=a;
a=b;
b=temp;
}
拓:引用(&)变量
//引用(&)变量
#include<iostream>
using namespace std;
int main(){
int j=0,k=10;
int &i=j; //变量i成为变量j的别名
cout<<"i:"<<i<<" j:"<<j<<endl;
cout<<&i<<" "<<&j<<endl;
i=k; //变量i获得变量k的值
k=12;
cout<<"i:"<<i<<" j:"<<j<<" k:"<<k<<endl;
cout<<&i<<" "<<&k<<endl; //证明 i=k 没有让变量i成为变量k的别名
return 0;
}
函数返回两个及以上的值(通过引用 / 指针 / 全局变量实现)
引用 调用
#include<iostream>
using namespace std;
void extremum(const int a[],int n,int &max,int &min);
int main(){
int max,min;
int a[]={12,34,56,7,3,99,67,45,3,80};
extremum(a,sizeof(a)/sizeof(a[0]),max,min);
cout<<"max="<<max<<" min="<<min<<endl;
return 0;
}
void extremum(const int a[],int n,int &max,int &min){
max=min=a[0];
for(int i=0;i<n;i++){
if(max<a[i]) max=a[i];
if(min>a[i]) min=a[i];
}
}
指针-法一
#include<iostream>
using namespace std;
int main(){
int *max,*min; //这个方法不能用函数(反正我不会),(具体原因分析在下面)
//麻烦且没必要
int a[]={12,34,56,7,3,99,67,45,3,80};
max=&a[0];
min=&a[0];
for(int i=0;i<sizeof(a)/sizeof(a[0]);i++){
if(*max<a[i]) max=&a[i]; //将max变为a[i]的别名
if(*min>a[i]) min=&a[i];
}
cout<<"max="<<*max<<" min="<<*min<<endl;
return 0;
}
/*****引用函数造成错误(无法输出)*****
#include<iostream>
void extremum(int a[],int n,int *max,int *min);
using namespace std;
int main(){
int *max,*min;
int a[]={12,34,56,7,3,99,67,45,3,80};
extremum(a,sizeof(a)/sizeof(a[0]),max,min);
cout<<"max="<<*max<<" min="<<*min<<endl; //无法输出
return 0;
}
void extremum(int a[],int n,int *max,int *min){
max=&a[0];
min=&a[0];
for(int i=0;i<n;i++){
if(*max<a[i]) max=&a[i]; //将max变为a[i]的别名
if(*min>a[i]) min=&a[i];
}
}
***************************************/
/*****原因分析*****/
//在extremum函数中sizeof(a)=4,主函数中sizeof(a)=40,
//反正它应该是发生了啥不为我所知的变化就是不行
#include<iostream>
using namespace std;
int main(){
int *a;
*a=8; //指针不能直接指向整数
cout<<*a<<endl; //无法输出
return 0;
}
#include<iostream>
using namespace std;
int main(){
int *a,i=8;
*a=i;
cout<<*a<<endl; //输出8
return 0;
}
指针-法二
#include<iostream>
void extremum(int a[],int n,int *max,int *min);
using namespace std;
int main(){
int a[]={12,34,56,7,3,99,67,45,3,80};
int max,min;
extremum(a,sizeof(a)/sizeof(a[0]),&max,&min); //通过引用(&)将整型int变为指针带入到函数中
cout<<"max="<<max<<" min="<<min<<endl;
return 0;
}
void extremum(int a[],int n,int *max,int *min){
*max=*min=a[0];
for(int i=0;i<n;i++){
if(*max<a[i]) *max=a[i];
if(*min>a[i]) *min=a[i];
}
}
指针-法三
#include<iostream>
int max,min;
void extremum(int a[],int n);
int main(){
int a[]={12,34,56,7,3,99,67,45,3,80};
extremum(a,sizeof(a)/sizeof(a[0]));
std::cout<<"max="<<max<<" min="<<min<<std::endl;
return 0;
}
void extremum(int a[],int n){
int *p;
max=min=*a; //对于数组a,*a的就是指向a数组中的第一个数
for(p=a+1;p<a+n;p++){ //p=a+n的意思是指针p指向数组a的最后一位的后一位
//从a+1即a数组中第二个数开始,因为max和min的初始化值为a[0]
if(max<*p) max=*p;
if(min>*p) min=*p;
}
}
全局变量
#include<iostream>
int _max,_min; //不能命名为 max(min) , max 和标准库的 std::max 同名了
void extremum(int a[],int n);
using namespace std; //就是有这个的时候,全局变量定义中不能有max和min
int main(){
int a[]={12,34,56,7,3,99,67,45,3,80};
extremum(a,sizeof(a)/sizeof(a[0]));
cout<<"max="<<_max<<" min="<<_min<<endl;
return 0;
}
void extremum(int a[],int n){
_max=_min=a[0];
for(int i=0;i<n;i++){
if(_max<a[i]) _max=a[i];
if(_min>a[i]) _min=a[i];
}
}
独立(常)引用
/* 非参数的独立引用很少使用,因为它们很容易混淆并会破坏程序的结构。
常(独立)引用(const 类型 &引用名=变量名;) */
#include <iostream>
using namespace std;
int main()
{
int j,k;
const int &i=j; //(独立)常引用,变量i成为变量j的一个别名
j=10;
cout<<"j="<<j<<" i="<<i<<endl; //输出 j=10 i=10
k=20;
//i=k; //错误,不允许通过常引用修改变量的值!
j=k; //通过j间接修改是可以的!
cout<<"j="<<j<<" i="<<i<<endl; //输出 j=20 i=20
}
返回引用
//实例:利用返回引用创建有界数组
#include<iostream>
int &put(int i); //将一个值放入数组中
int get(int i); //从数组中获得一个值
int error=-1; //不能使用const修饰符,因为它是返回引用的返回值(错误情况)
const int MAX=10; //定义最大数组长度(数组的最大下标为MAX-1)
int a[MAX];
using namespace std;
int main(){
put(0)=5;
put(2)=20;
put(9)=1314;
cout<<a[0];
cout<<a[2]<<endl;
cout<<a[9]<<endl;
//下面有意产生错误
put(MAX+2)=6; //数组越界
int i=get(MAX+2); //数组越界
return 0;
}
int &put(int i){
if(i>=0&&i<MAX){
return a[i];
}
else{
cout<<"边界错误(put)"<<endl;
return error;
}
}
int get(int i){
if(i>=0&&i<MAX){
return a[i];
}
else{
cout<<"边界错误(get)"<<endl;
return error;
}
}
不要返回局部变量引用!!!(虽然编译可以通过)
#include <iostream>
int &func();
using namespace std;
int main() {
cout<<(func()=5)<<func()<<endl; //输出520
}
int &func() {
int x=20; //局部变量
return x; //编译可以通过,但不能使用!!
//warning C4172: returning address of local variable or temporary
}
引用变量概念
/*
1.引用变量定义时必须进行初始化。
在程序中对引用的存取都是对它所引用的变量的存取。也不能再指向别的变量!
2.引用与指针不同:
指针的内容或值是某一变量的内存单元地址,
而引用则与初始化它的变量具有相同的内存单元地址。
3.返回引用必须保证返回的引用变量有合法的内存空间,并且不在函数的运行栈中。
一般只能返回全局变量和静态变量。
4.使用const常引用的目的就是提高性能(不需要制作数据的副本)。
使用引用参数而不加const就应该认为该参数将被修改!
5.当函数需要返回多于1个值,或希望修改实参的值时必须使用引用调用。
6.如果条件允许,就应将引用形参声明为const形参,
这样可以避免无意间修改数据而导致编程错误,
此外使用const形参使得函数能够接受const和非const实参,否则只能处理非const数据。
eg.
#include<iostream>
void func(const int &i);
using namespace std;
int main(){
int a=1314;
func(a);
return 0;
}
void func(const int &i){
cout<<i<<endl;
}
7.对比总结:
void func1(int n){
//可以修改n,但值不会返回调用者
} //可用值或变量调用
void func2(int &n){
//可以修改n,值会返回调用者
} //只能用同类型的变量调用该函数
void func3(const int &n){
//根本就不允许修改n
} //可用值或变量调用
//func1/2/3具体如下
#include<iostream>
int func1(int n);
using namespace std;
int main(){
cout<<"4!="<<func1(4)<<endl;
int n=5;
cout<<"5!="<<func1(n)<<endl;
return 0;
}
int func1(int n){ //可用值或变量n调用,2个n(形参和实参)的地址是不一样的!
int fac;
for(fac=1;n>0;n--){
fac*=n;
}
return fac;
} //可以修改n,但值不会返回调用者
#include<iostream>
int func2(int &n);
using namespace std;
int main(){
//cout<<"4!="<<func2(4)<<endl; 错误,只能同类型调用
int n=5,fac=func2(n);
cout<<n<<"!="<<fac<<endl; //输出 0!=120
cout<<&n<<endl;
return 0;
}
int func2(int &n){ //只能用同类型的变量调用该函数
cout<<&n<<endl; //func2的n和main的n的(实参地址传递给形参)地址一致!
int fac;
for(fac=1;n>0;n--){
fac*=n;
}
return fac;
} //可修改n,修改后的值会返回调用者
#include<iostream>
int func3(const int &n);
using namespace std;
int main(){
cout<<"4!="<<func3(4)<<endl;
int n=5,fac=func3(n);
cout<<n<<"!="<<fac<<endl;
return 0;
}
int func3(const int &n){ //可用值或变量调用
int fac=1;
for(int i=n;i>0;i--){ //func2的n和main的n的(实参地址传递给形参)地址当然也一致!
fac*=i;
}
return fac;
} //根本就不允许修改n
/*拓:实参和形参
1、值传递
实参是变量,表达式等值。
find(int x){}
y= find(z);
上面的例子中,z是实参,x是形参。x变z不变。
在值传递过程中,实参和形参位于内存中两个不同地址中,
实参先自己复制一次拷贝,再把拷贝复制给形参。
所以,在值传递过程中,形参的变化不会对实参有任何的影响。
2、地址传递(也称引用传递)
实参是指针。
在函数调用的时候,实参传递给你的是指针地址,地址一样也就意味着实参和形参是一样的,
当你的形参发生改变时,实参也会发生改变。
find(int &x){}
y= find(z);
上面的例子中,z是实参,x是形参。z随x而改变。
3、const引用传递
find(const int &x){}
y= find(z);
上面的例子中,z是实参,x是形参。z不随x而改变。
(const引用和值传递不一样)在值传递中要进行两次拷贝,浪费内存资源是相当可耻的,
const的出现有效避免了这种情况的出现,只需拷贝一次就够了。 */
15.内联函数inline
inline只是一种要求,通常应该是比较简单的函数。取代C的“宏定义”。
内联函数默认是静态的(仅用于本文件)
下列3种情况编译器不会将函数处理成内联函数:
1、函数内有循环语句;
2、函数太复杂(取决于编译器);
3、程序中有取该函数地址的语句。
inline(内联)函数一般直接放到头(.h)文件中。
因为内联函数要在调用点展开,所以编译器必须随处可见内联函数的定义,要不然就成了非内联函数的调用了。
所以,这要求每个调用了内联函数的文件都出现了该内联函数的定义。
inline必须与函数定义体放在一起才能使函数成为内联,仅将inline放在声明前面不起任何作用。
所以说inline是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。
一般地,用户可以阅读函数的声明,但是看不到函数的定义。
尽管在大多数教科书中内联函数的声明、定义前面都加了inline 关键字,但inline最好不出现在函数的声明中。
eg.
如下风格的函数不能成为内联函数:
inline void func(int x);
void func(int x){
//...
}
而如下风格的函数可以成为内联函数:
void func(int x);
inline void func(int x){
//...
} //inline与函数定义体放在一起
16.默认参数(带默认参数的函数)
百度百科:
在计算机科学中,内联函数(有时称作在线函数或编译时期展开函数)是一种编程语言结构,用来建议编译器对一些特殊函数进行内联扩展(有时称作在线扩展);也就是说建议编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省了每次调用函数带来的额外时间开支。
但在选择使用内联函数时,必须在程序占用空间和程序执行效率之间进行权衡,因为过多的比较复杂的函数进行内联扩展将带来很大的存储资源开支。
另外还需要特别注意的是对递归函数的内联扩展可能引起部分编译器的无穷编译
/*在C++中,可以为函数参数指定默认值,
默认参数必须在函数第一次声明中进行声明,通常是在函数原型中声明. */
特别的:当函数在前面的声明中已经有了默认参数,则在函数原型(定义)中就不能带有默认参数
//只有一个默认参数时要放在后面 eg. void func(int x,int y=10);
//带默认参数的函数实例
#include<iostream>
using namespace std;
void func(int x=10,int y=20){ //默认参数必须在函数第一次声明中进行声明
//只有一个默认参数时要放在后面
//eg. void func(int x,int y=10);
cout<<"x="<<x<<" y="<<y<<endl;
}
int main(){
func(1,3);
func(1);
func();
return 0;
}
#include <iostream>
using namespace std;
void func(int x=10,int y=20); //默认参数必须在函数第一次声明中进行声明
int main(){
func(1,3);
func(1);
func();
return 0;
}
void func(int x,int y){ //这里不能带默认参数
cout<<"x="<<x<<" y="<<y<<endl;
}
//拓:默认参数的应用之一就是函数重载的简写形式
/* 例如:要编写strcat函数的两个版本:
void mystrcat(char *,char *,int);
void mystrcat(char *,char *);
可以使用函数重载,但用默认参数实现更简单(只要编写一个版本就可以)*/
//使用默认参数
#include<iostream>
#include<cstring>
using namespace std;
void mystrcat(char *s1,char *s2,int len=0);
//使用默认参数统一两个版本的mystrcat
int main(){
char str1[]="This is a test";
char str2[]="0123456789";
mystrcat(str1,str2,5);
cout<<str1<<endl;
strcpy(str1,"This is a test"); //重置str1
mystrcat(str1,str2);
cout<<str1<<endl;
return 0;
}
void mystrcat(char *s1,char *s2,int len){
//不能写为void mystrcat(char *s1,char *s2,int len=0) 否为报告重定义错误
while(*s1) s1++; //等价于while(*s!='\0') s++;
//找到s1的末尾
if(len==0) len=strlen(s2);
while(*s2&&len){
*s1++=*s2++; //赋值后指针s1和s2再一起指向下一位
//将s2接到s1末尾
len--;
}
*s1='\0'; //在s1末尾加上null终止符('0')
}
17.函数重载
函数重载是C++实现多态性的一种方法
每个重载函数的参数类型和(或)数量必须不同;正常执行相似的操作;返回值可以不同。
#include<iostream>
int func(int x);
int func(int x,int y);
void func(double x);
using namespace std;
int main(){
int x=10,y=20;
double p=3.14;
func(x);
func(x,y);
func(p);
return 0;
}
inline int func(int x){
cout<<"In func(int),x is "<<x<<endl;
}
inline int func(int x,int y){
cout<<"In func(int,int),x is "<<x<<", y is "<<y<<endl;
}
inline void func(double x){
cout<<"In func(double),x is "<<x<<endl;
}
在形参和实参之间类型不能直接匹配时进行类型自动转换。
//实际返回的值只能比你定义的返回值精度低,才合法,否则不合法
//自动类型转换和函数重载实例
#include<iostream>
using namespace std;
void func(int x);
void func(short x);
void func(double x);
int main(){
int i=1314;
short s=520;
double d=3.1415;
//long l=1000; 无法通过func函数打印l的值
float f=1.4; //实际返回的值只能比你定义的返回值精度低,才合法,否则不合法
func(i);
func(s);
func(d);
func(f);
return 0;
}
void func(int x){
cout<<"In func(int),x is "<<x<<endl;
}
void func(short x){
cout<<"In func(short),x is "<<x<<endl;
}
void func(double x){
cout<<"In func(double),x is "<<x<<endl;
}
以下实例中的函数func不是重载:
void func(int);
void g(){
void func(double); //很容易引起错误
//...
func(1); //调用func(double);
//...
}
但下述实例的函数func是重载:
void func(int);
void func(double);
void g(){
//...
func(1); //调用func(int);
//...
}