一、引用:引用相当于变量的别名,引用只能引用变量,不可引用常量和表达式。初始化(定义引用时就要初始化)后,它就一直引用该变量,不会再引用其他变量了。
引用程序1:
#include<iostream>
using namespace std;
void main(){
int n=4;
int&r=n;
cout<<r<<endl;//输出4
n=5;
cout<<r<<endl;//输出5
r=6;
cout<<r<<" "<<n<<endl;//输出6 6
}
引用程序二:
#include<iostream>
using namespace std;
void swap(int&c,int &d);
void main(){
int a=4,b=5;
swap(a,b);
cout<<a<<" "<<b<<endl;
//注意这里和c语言不同,主要是因为这是引用(别名)。
//输出为5 4,表示交换值成功。
}
void swap(int&c,int &d){
int temp=0;
temp=c;
c=d;
d=temp;
}
引用程序三:
#include<iostream>
using namespace std;
int n=4;
int &setvalue(){
return n;
}
void main(){
setvalue()=40;
cout<<n<<endl;
//注意,这里有点小特殊的形式,它是对返回值变量的引用。
//因为返回值变量为n,所以n的值变为40。
//输出为40。
}
引用程序四:
#include<iostream>
using namespace std;
void main(){
int n=4;
const int &r=n;
cout<<r<<endl;
n=5;
//r=4;编译错误,常引用不可以赋值,const存在。
cout<<n<<" "<<r<<endl;
//输出为5 5,说明可以通过修改原变量修改常引用变量的值。
}
二、常量指针一:
const int *r=&n;*r=4;//为编译错误。
常量指针二:
不可以把常量指针赋值给非常量指针,但是可以把非常量指针赋值给常量指针。
注意这个顺序问题。
#include<iostream>
using namespace std;
void main(){
const int*p1;
int *p2;
p2=p1;//编译错误,不可以把变量指针变为常量指针。
*p2=5;
cout<<*p2<<endl;
}
特殊情况,强制类型转换是可以的,但是无输出,小心点别用了。
#include<iostream>
using namespace std;
void main(){
const int*p1;
int *p2;
p2=(int*)p1;
*p2=5;
cout<<*p2<<endl;
}
反过来不报错,但是却无输出,以后还是别用这种类型转换了。
#include<iostream>
using namespace std;
void main(){
const int*p1;
int *p2;
*p2=5;
p1=p2;
cout<<*p2<<" "<<*p1<<endl;
}
总结:常引用和常指针的值都是不可以通过常引用或常指针本身进行修改的,但是常引用可以通过改变原变量修改值。
三、动态分配内存:
#include<iostream>
using namespace std;
void main(){
int *pn=new int;//动态分配一个空间,返回首地址,注意这是*pn,是指针类型。
*pn=5;
cout<<*pn<<endl;//输出为5
delete pn;//释放动态分配的单个变量的空间。
int i=5;
int*pnn=new int[i*20];
//注意i意味着分配变量空间的时候可以通过变量定义空间大小。
//不用new则不行。
pnn[0]=20;
pnn[100]=20;//编译时无错,运行(Debug)时会产生数组越界报错。
delete[]pnn;//释放动态分配的数组空间。
}
四、内联函数:
函数调用是有时间开销的。如果函数本身只有几条语句,执行非常快,而且函数被反复执行很多次,相比之下调用函数所产生的开销会比较大,为了减少函数调用的开销引入了内联函数机制。编译器处理对内联函数的调用语句时,是将整个函数的额代码插入到调用语句中,而不会产生调用函数的语句。因此可以减少调用函数的开销。
inline int max(int a,int b){
if(a>b) return a;
else return b;
}
五、函数重载
一个或多个函数,名字相同,然而参数个数或参数类型不相同,这叫做函数的重载。
int max(doubel f1,double f2);
int max(int n1,int n2);
int max(int n1,int n2,int n3);
同名情况下由编译器决定调用那个函数。但是要注意二义性。如下:
max(3,2.4);这个是上述max函数中的第一个还是第二个呢?
函数的缺省参数
C++中,定义函数的时候可以让最有白牛的连续若干个参数有缺省值,那么调用函数的时候,若相应位置不写参数,参数就是缺省值。
void func(int x1,int x2=2,int x3=3){}
func(10 );
func(10,8);//等效于func(10,8,3);
func(10, ,8)//这种形式是错误的,只可以将最右边的连续若干个参数缺省。
总结:划重点。注意区分函数重载和函数缺省。
函数参数可缺省的目的在于提高程序的可扩充性。即如果某个写好的函数要添加新的参数,而原先那些调用该函数的语句,未必需要使用新增的参数,那么为了避免对原先哪些函数调用语句的修改,就可以使用缺省参数。
六、类与对象
#include<iostream>
using namespace std;
class cCar{
public:
int price;
void setprice(int p);
};
void cCar::setprice(int p){
price=p;
}
void main(){
cCar car;
car.setprice(20000);
}
(一)、this指针
其作用就是指向成员函数所作用的对象,
非静态成员函数中可以直接使用this来代表指向该函数作用对象的指针。
例子一:
#include<iostream>
using namespace std;
class complex{
public:
double real,imag;
void print(){
cout<<real<<","<<imag<<endl;
}
complex(double r,double i):real(r),imag(i){}
//real(r)含义是real=r,imag(i)的含义是imag=i,
//这样定义比在构造函数内定义快;
complex addone(){
this->real++;//this是指针。
this->print();//this是指针。
return *this;
}
};
void main(){
complex c1(1,1),c2(0,0);
c2=c1.addone();
}
例子二:
#include<iostream>
using namespace std;
class A{
int i;
public:
void hello(){
cout<<"hello"<<endl;
}
};
void main(){
A*p=NULL;
p->hello();
//即使为NULL,看似没有初始化,
//实际上是默认构造函数也给初始化了对吗?
//输出为:hello。
}
静态成员函数中不能使用this指针。
因此静态成员函数的真实的参数的个数,就是程序中写出的参数的个数。
而类的非静态成员函数,真实的参数比所写的参数多1。
(二)、类的成员
1.静态成员:在定义前面加了static关键字的成员。
2.普通成员变量每个对象有各自的一份存储空间,而静态成员变量一共就一份,为所有对象共享。
class cmyclass{
int n;
static int s;
};
sizeof运算符不会计算静态成员变量,因此sizeof(cmyclass)=4,即int n的大小。
静态成员函数并不具体作用于某个对象,因为它无需对象就可调用,因此哪怕一个对象都不存在,类的静态成员变量也存在。
类的静态成员变量相似于全局变量。静态成员函数本质上是全局函数。
3.如何访问静态成员?
(1)crectangle::printtotal():
(2)crectangle r;
r.printtotal();
(3)crectangle*p=&r;
p->printtotal();
代码:
#include<iostream>
using namespace std;
class crectangle{
private:
int w,h;
static int ntotalarea;
static int ntotalnumber;
public:
crectangle(int w_,int h_);
~crectangle();
static void printtotal();
};
crectangle::crectangle(int w_,int h_){
w=w_;
h=h_;
ntotalnumber++;
ntotalarea+=w*h;
}
crectangle::~crectangle(){
ntotalnumber--;
ntotalarea-=w*h;
}
void rectangle::printtotal(){
cout<<ntotalnumber<<","<< ntotalarea<<endl;
}
int crectangle::ntotalnumber=0;
int crectangle::ntotalarea=0;
//必须在定义类的文件中对静态成员变量进行一次性说明或初始化,
//否则编译能通过,链接不能通过。
void main(){
crectangle r1(3,3),r2(2,2);
crectangle::printtotal();
r1.printtotal();
return 0;
}
//输出结果为2,13
// 2,13。
补充:在静态成员函数中,不可以访问非静态成员变量,也不能调用非静态成员函数。
另外,为了解决临时隐藏对象在消亡时会调用析构函数,在生成时却没有调用构造函数,
导致静态变量的值未增加,所以添加一个复制构造函数。
crectangle::crectangle(crectangle&r){
w=r.w;
h=r.h;
ntotalnumber++;
ntotalarea+=w*h;
}
(三)封闭类:封闭着类的类
#include<iostream>
using namespace std;
class ctyre{//轮胎类
private:
int radius;
int width;
public:
ctyre(int r,int w):radius(r),width(w){
cout<<"ctyre start"<<endl;
}
~ctyre(){
cout<<"ctyre end"<<endl;
}
};
class cengine{
int engineprice;
public:
cengine(int p){
engineprice=p;
cout<<"cengine start"<<endl;
}
~cengine(){//析构函数必须括号内为空。
cout<<"cengine end"<<endl;
}
};//引擎类
class ccar{
private:
int price;
ctyre tyre;
cengine engine;
public:
ccar(int p,int tr,int tw);
//注意,这里是分号,不可以是大括号{}啊,那样就没办法外部定义了啊。
//注意,这里不可以为空,如果是空的{},那么编译器会懵掉的,因为它不知道ctyre构造函数每个参数会对应哪个喵,是p呢,还是tr呢,还是tw呢,总共六个选择呢亲。
//让我们用封闭类的构造函数的初始化列表吧。
~ccar(){
cout<<"ccar end"<<endl;
}
};
ccar::ccar(int p,int tr,int tw):engine(p),tyre(tr,tw){//这就是初始化列表喵,因为该类里面有封闭其他类的对象,所以构造函数千万要初始化列表啊。
cout<<"ccar start"<<endl;
};
void main(){
ccar car(1,2,3);
}
来,除了上述注释需要注意以外,让我们看一下运行结果:
ctyre start
cengine start
ccar start
ccar end
cengine end
ctyre end
这说明程序先运行被封闭着的类对象的构造函数,再运行这个大类的构造函数,
然而析构函数却反过来了,按照逻辑推也应该是反过来啊。像拆礼物盒子一样咯。
先析构大的包装类,再析构小的类对象。
(四)友元函数和友元类
1.友元函数:一个类的友元函数可以访问该类的私有成员。
#include<iostream>
using namespace std;
class ccar;//提前声明方便使用具体细节后面写。
class cdriver{
public:
void modifycar(ccar*pcar);
};
class ccar{
private:
int price;
int num;
friend int mostexpensivecar (ccar cars[],int total);//友元函数
friend void cdriver::modifycar(ccar*pcar);//友元函数
public:
ccar(){
this->num=0;
}
void setprice(int p){
this->num++;
if(this->num==1){
this->price=p;
}else{}
}
};
void cdriver::modifycar(ccar*pcar){
pcar->price+=1000;//调用了ccar的私有变量。
};
int mostexpensivecar(ccar cars[],int total){
int tmpmax=-1;
for(int i=0;i<total;i++){
cout<<cars[i].price<<endl;
if(cars[i].price>tmpmax){
tmpmax=cars[i].price;
}
}
return tmpmax;
}
int main(){
int total=5;
int tmp=0;
ccar*cars=new ccar[total];//动态分配内存
for(int i=0;i<total;i++){
cin>>tmp;
cars[i].setprice(tmp);
}
cars[4].setprice(1000);
int max=mostexpensivecar(cars,total);
cout<<"max="<<max<<endl;
delete[]cars;
return 0;
}
//输入1 2 3 4 5
//输出5
只检验了第一个友元函数,第二个没检验。
2、友元类
#include<iostream>
using namespace std;
class ccar{
private:
int price;
friend class cdriver;//友元类定义
};
class cdriver{
public:
ccar mycar;//友元类内部借朋友之助。
void modifycar(){
mycar.price+=1000;
}
};
void main(){
//具体应用自己看情况填自己看情况补充。
}
封闭类的复制构造函数先略。
(五)常量成员函数
常量对象只能使用构造函数,析构函数和有const说明的常量成员方法。
常量成员方法(函数)内部不能改变属性的值,也不能调用非常量成员函数。
#include<iostream>
using namespace std;
class sample{
private:
int value;
public:
void func(){ }
sample(){ }
void setvalue()const{
//value=0;错误,改变了属性value的值。
//func();错误,调用了非常量成员函数。
}
};
void main(){
//具体应用自己看情况填自己看情况补充。
const sample obj;
obj.setvalue();
//正确,因为setvalue属于常量成员方法函数,所以常量对象可以调用。
}
两个函数,名字和参数都一样,但是一个是const,一个不是,算重载。
如果想在const成员函数中修改成员变量,需在成员变量前加mutable。