3.1内联成员函数与成员函数重载
3.1.1内联成员函数
实现方式
- inline+成员函数
- 先申明,然后整个函数体都出现在类定义内部
class b{
inline void func1(){}//第一种定义内联成员函数方式
void func2(){}
void B::func2(){cout<<" "<<endl}//第二种定义内联成员函数方式
};
3.1.2成员函数重载及参数缺省
#include <iostream>
using namespace std;
class location{
private:
int x,y;
public:
void init(){x=0;y=0;}
void init(int x1, int y1){x=x1, y=y1}//这两行是同一个函数,是对成员函数Init()的重载
void setx(int val=5){x=val;}//这里变量val缺省为5,可以不填
int valuex(){return x;}
};
int main(){
location a;
cout<<a.valuex()<<endl;
a.setx();//ok,有缺省变量,x变成5;
cout<<a.valuex()<<endl;
return 0;
}
0
5
- ps:在使用缺省函数参数时要注意避免函数重载的二义性
class location{
private:
int x,y;
public:
void init(int val=5;){x=val;y=val;}
int init(){x=5, y=33}
};
int main(){
location a;
a.init();//此时,编译器无法判定调用哪个init
return 0;
}
3.1.3构造函数
- 名字与类名相同,可以有参数,没有返回值
- 用处是将对象初始化,但具体工作可以自定,可以干任何事
- 如果没有定义构造函数,则编译器会自动产生一个无参数构造函数,不做任何事情
- 只在对象生成的时候调用,一旦生成后不能调用
- 构造函数可以重载,可以根据参数不同调用不同的构造函数
class complex{
private:
double real,imag;
public:
complex(double r,double i){real=r;imag=i;}
complex(int a){real=a;imag=0;}
complex(complex a,complex b){real=a.real+b.real; imag=a.imag+b.imag}//这三个就是对构造函数的重载
};
int main(){
complex a(3);//调用第二个构造函数
complex b(2.5,2.8)//调用第一个构造函数
complex c(a,b)//调用第三个构造函数
return 0;
}
- 构造函数再数组中的使用
class test{
public:
test(int n){}
test(int n,int m){}
test(){}
};
int main(){
test array[3]={1,test(1,2)}//数组中三个元素分别用第1,2,3个构造函数初始化
return 0;
}
3.1.4复制构造函数
- 参数只能为对同类对象的引用
- 如果没有定义复制构造函数,则用编译器生成的默认复制构造函数
- 如果某函数的参数或者返回值是一个类的对象,则该函数被调用时候,复制构造函数也会被调用
class complex{
private:
double real,imag;
public:
complex(){real=1;imag=1;}
complex(const complex &c){
real=c.real;
imag=c.imag;
}//复制构造函数
complex(compex a){}//错,参数只能用引用
void setreal(A a){real=a.real;}//参数为A对象的时候,会调用复制构造函数
A setimag(){
imag=5;
A b;
return b;
}//返回值是A的对象的时候,会调用复制构造函数
};
int main(){
complex a;
complex b(a);//调用复制构造函数
complex c=a;//调用复制构造函数
c.setreal(a);//这里会调用复制构造函数
c.setimag();
return 0;
}
3.1.5类型转换构造函数
- 实现类型自动转换
- 只有一个参数
- 建立一个临时的对象
class complex{
private:
double real,imag;
public:
complex(double r,double i){
real=r;
imag=i;
}
complex(int i){real=i;imag=0;}//类型转换构造函数
};
int main(){
complex c1;
c1=9;//这句话的时候9会被自动转换为一个临时的complex对象
return 0;
}
3.1.6析构函数
- 名字与类名相同
- 前面需要加 ~
- 没有参数和返回值,在成员消亡的时候调用
- 一个类只有一个析构函数
- 没有定义析构函数,编译器会自动生成一个默认的析构函数
class string{
private:
char* p;
public:
string(){p=new char[10];}
~string(){delete [] p;}
};
int mian(){
string a[5];
return 0;//当程序结束的时候调用析构函数,数组中的每个元素都要调用一次析构函数,这里要调用五次
}
构造函数和析构函数的调用时机
- 先构造的后消亡
class a{};
int main(){
a b;
a c;
return 0;
}//程序结束时候,先调用对象c的析构函数,再调用对象b的析构函数
3.1.7静态成员变量和静态成员函数
- 在普通的成员函数/成员变量前面加上static关键字
- 静态成员变量每个类只有一个,所有该类的对象共享拥有这个变量,都可以对它进行修改。
- 可以不通过对象调用静态成员
- 静态变量需要在所有函数外面申明,此时可以初始化
- 在静态成员函数中不可访问非静态成员变量和非静态成员函数
class rectangle{
public:
static int a;
static void print(){}
};
int main(){
rectangle::print();//可以不通过对象调用静态成员
return0;
}
e.g
#include <iostream>
using namespace std;
class crectangle{
private:
int w,h;
static int area;
static int number;
public:
crectangle(int w_,int h_){w=w_;h=h_;area+=w*h;number++;}//area和number都是公用的
~crectangle(){number--;area-=w*h;}//消亡的时候要减去它的面积,不然new的对象会出错
static void print(){cout<<number<<" "<<area<<endl;}
//static void print(){cout<<area<<endl;}这句话错,不能在静态成员函数中调用静态成员变量
};
int crectangle::area=0;//静态成员变量必须在所有函数外面申明,可以初始化也可以不初始化
int crectangle::number=0;
int main(){
crectangle r1(3,3),r2(2,2);
crectangle::print();//可以通过类名调用静态成员函数
r1.print();//也可以通过对象调用静态成员函数
return 0;
}
2 13
2 13
3.1.8成员对象和封闭类
- 一个类的成员变量是另一个类的对象,称之为成员对象
- 包含成员对象的类叫做封闭类
- 初始化列表
类名::构造函数(参数表):成员变量1(参数表),成员变量2(参数表)… - 先执行成员对象的构造函数,再执行封闭类的构造函数
- 先执行封闭类的析构函数,再执行成员对象的析构函数
#include <iostream>
using namespace std;
class ctyre{
private:
int r,w;
public:
ctyre(int r_,int w_):r(r_),w(w_){}//冒号后面跟成员初始化,称为初始化列表,可以对变量初始化
};
class cengine{};
class ccar{
private:
int price;
ctyre tyre;//成员对象
cengine engine;//成员对象
public:
ccar(int p,int tr,int w):price(p),tyre(tr,w){}//对有成员对象的类进行初始化要对其对象进行初始化。
};
int main(){
ccar car(20000,17,225);
return 0;
}
3.1.9友元
友元函数
- 一个类的友元可以访问该类的私有成员
- 需要在类内部对一个函数声明,即在前面加上friend
class ccar{
private:
int price;
friend void setprice(ccar a);//声明友元函数
};
void setprice(ccar a){a.price=5;}
int main(){
ccar b;
b.price=5;//不能访问私有成员
setprice(b);//友元函数可以修改私有成员
return 0;
}
友元类
- 可以声明一个类是友元
- 若A是B的友元类,A的成员函数可以访问B的私有成员
class ccar{
private:
int price;
friend class cdriver;
};
class cdriver{
public:
ccar mycar;
void set(){
mycar.price+=1000;//友元类的成员函数可以访问私有成员
}
};
3.1.10this指针
- 非静态成员函数中可以使用this*指向成员函数所作用的对象
class ccar{
private:
int price;
public:
print(){cout<<price<<endl;}
ccar addone(){
this->price++;
this->print();//可以通过this指针调用当前对象的成员函数
return *this//可以通过*this返回当前对象
}
};
3.1.11常量对象,常量成员函数和常引用
- 若2不希望某个对象的值被修改,可以生成对象的时候在前面加const
- 常量对象只能调用常量成员函数
class a{};
const a b;
- 可以再成员函数的说明后面加const成为常量成员函数
- 常量成员函数不能修改成员变量的值(静态成员变量除外),也不能调用同类的非常量成员函数(静态成员函数除外)
class a{
public:
int c;
void aaa(){}
void get() const {
c=5;//错误,不能修改非常量成员变量的值
aaa();//错误,不能调用非常量成员函数
}
};
- 常量成员函数可以重载
class a{
public:
int c;
a(){c=1;}
int get() const{return c;}
int get() {return 2*c;}
};
int main(){
const a b;
a d;
cout<<b.get()<<endl;//会自动调用常量常量成员函数get()
cout<<d.get()<<endl;//调用非常量成员函数get()
return 0;
}
- 可以将参数写成 const int& a 的形式,称之为常引用,不能通过函数修改其值,经常用到。
void a(const int& a){
a=5;//错误,不能修改常引用的值
}