更多参考其他文档菜鸟文档W3C微软C++文档

类和对象基础

类和对象基本概念

  • 类的成员函数和类的定义分开写
//类的成员函数和类的定义分开写
#include <iostream>

using namespace std;

class CRectangle //定义一个类
{
public:
int w,h;
int Area(); //此处函数可以先声明,并不写出函数体
int Perimeter(); //此处函数可以先声明,并不写出函数体
void Init(int w_,int h_);
};

int CRectangle::Area(){ //类中的函数实体可以写到函数外
return w * h;
}
int CRectangle::Perimeter(){
return 2 * (w + h);
}
void CRectangle::Init(int w_,int h_){
w = w_; h = h_;
}
int main()
{
return 0;
}
  • 类成员的可访问范围
//在类的定义中,用下来关键字来说明类成员的可被访问的范围
- private : 私有成员,只能在函数内访问
- public :公有成员,可以在任何地方访问
- protected:保护成员,以后再说
//出现的顺序没有限制

class className {
private:
私有属性和函数
public
公有属性和函数
protected:
保护属性和函数
};

//一般的,如果某个类的成员前面没有上述关键字,则被认为是私有成员

class Man{
int nAge;//私有
char szName[20];//私有
public
void SetName(char * szName) //公有
{
strcpy(Man::szName,szName);
}
};

  • 访问范围
//在当前类、函数的成员函数内部,能够访问:
- 当前对象的全部函数、属性;
- 同类其它对象的全部属性、函数;
//在类的成员函数以外的地方,只能够访问该类对象的公有成员,私有成员不能访问

class CEemployee {
private:
char szName[30];//名字
public
int salary;//工资
void setName(char * name);
void getName(char * name);
void averageSalary(CEmployee e1,CEmployee e2);
};

void CEmplotyee::setName(char * name){
strcpy(szName,name);//可以访问私有的szName
}
void CEmployee::getName(char * name){
strcpy(name,szName); //ok
}
void CEmployee::averageSalary(CEmployee e1,CEmployee e2){
cout<<e1.szName;//ok
salary = (e1.salary + e2.salary)/2;
}
int main()
{
CEmployee e;
strcpy(e.szName,"Tom1234567889");// 编译错,不能访问私有成员
e.setName("Tom"); // ok
e.salary = 5000; //ok
return 0;
}

//隐藏

int main()
{
CEmployee e;
strcpy(e.szName,"Tom1234567889");//编译错,不能访问私有成员
e.setNName("Tom"); //ok
e.salary = 5000; // ok
return 0;
}
//设置私有成员的机制叫“隐藏”
“隐藏”的目的是强制对成员变量的访问一定要通过成员函数进行,那么以后成员变量的类型等属性修改后,只需要更改成员函数即可。否则,所有直接访问成员变量的语句都需要修改。

//例--
当一个程序移植到小内存的设备上时,会有例如将 szName[30] 改为 szName[5] 的需求,如果不在程序中设置为私有成员,那么程序中会有很多下面这样的语句:
strcpy(e.szName,"Tom1234567889");
此时,相关的语句也会报错,要一个一个修改,设置为私有后,我们就不会有上面的语句出现,可以通过如setName函数 来实现:
e.setName("Tom12345678909887");
这样,即便有这样的需求我们只需要将set函数做修改就行


成员函数的重载及参数缺省

//成员函数的重载及参数缺省
//成员函数可以带缺省参数
#include<iostream>
using namespace std;

class Location {
private :
int x,y;
public:
void init(int x=0,int y=0); //函数中有两个缺省参数
void valueX(int val){x = val;}
int valueX(){return x;} //这里用了重载,即两个函数名相同,但是参数不同
}
int main(){
Location A,B;
A.init(5); //少了的参数用缺省值,0
A.valueX(5); //调用时,注意避免二义性
cout<<A.valueX();
return 0;
}

//二义性报错!!
//二义性报错!!
class Location {
private :
int x,y;
public:
void init(int x=0,int y=0);
void valueX(int val = 0){x = val;} //这里使用了缺省参数0
int valueX(){return x;} //这里其实也是相当于为0
}
int main(){
Location A,B;
A.init(5);
A.valueX(); //如果此时调用,就会报错,存在二义性,计算机不知道调用哪个函数
cout<<A.valueX();
return 0;
}

构造函数(constructor)

构造函数是成员函数的一种,名字 与 类名 相同,可以有参数,不能有返回值(void 也不行)

作用:对对象进行初始化,如给成员变量赋初值

如果定义类时没有写构造参数,则编译器生成一个默认的无参数的构造函数

默认构造函数无参数,不做任何操作

//基本概念
类如果定义了构造函数,则编译器不生成默认的无参数的构造函数
对象生成时,构造函数自动被调用。对象一旦生成,就再也不能在其执行构造函数
一个类可以有多个构造函数
//为什么需要构造函数
构造函数执行必要的初始化工作,有了构造函数,就不必专门写初始化函数,也不用担心忘记调用初始化函数。
对象没有被初始化,容易出错

//例1
class Complex{
private:
double real,imag;
public:
void Set(double r,double i);
};//编译器自动生成默认构造函数

Complex cl;//默认构造函数被调用
Complexa * pc = newComeplex;//默认构造函数被调用

//例2
class Complex{
private:
double real,imag;
public:
Complex(double r,double i = 0);
};
Complex::Complex(double r,double i){
real = r;imag = i;
}
Complex c1; //error 缺少构造函数的参数
Complex * pc = new Complex; //error 也是因为没有参数
Complex C1(2); //OK 提供了参数,i 是 = 0的缺省参数,所以传一个参数就可以
Complex c1(2,4),c2(3,5);
Complex * pc = new Complex(3,4);

一个类可以有多个构造函数,只要这些类的参数个数或类型不同就可以,类似函数重载,也用于初始化

class Complex{
private:
double real,imag;
public:
void Set(double r,double i);
Complex(double r,double i); //重载的关系
Complex(double r); //重载的关系
Complex(Complex c1,Complex c2); //重载的关系
};
Complex::Complex(double r,double i) //构造函数1
{
real = r; imag = i;
}
Complex::Complex(double r) //构造函数2
{
real = r; imag = 0;
}
Complex::Complex(Complex c1,Complex c2); //构造函数3
{
real = cl.real + c2.real; //对应相加
imag = cl.imag + c2.imag;
}
Complex c1(3); //对应构造函数2,表示赋初始值为(3,0)
Complex c2(1,0); //对应构造函数1,表示赋初始值为(1,0)
Complex c3(c1,c2); //对应构造函数3,表示赋初始值为(4,0)

构造函数在数组中的使用

#include <iostream>

using namespace std;

class CSample {
int x;
public:
CSample(){ //无参数构造函数
cout<<"Constructor 1 Called"<<endl;
}
CSample(int n){ //有参构造函数
x = n;
cout<<"Constructor 2 Called"<<endl;
}
};

int main()
{
CSample array1[2]; // 2个变量都 没有 参数,则默认使用没给参数的构造函数
cout<<"step1"<<endl;

CSample array2[2] = {4,5};// 2个变量都 有 参数,则默认使用给参数的构造函数
cout<<"step2"<<endl;

CSample array3[2] = {3};
//一个有参数,一个无参数,则有参数的用有参构造函数初始化,无参的用无参构造函数初始化
cout<<"step3"<<endl;

CSample *array4 = new CSample[2]; //没有参数用无参
delete []array4;
return 0;
}

//分割线----------------------------------------------------
class Test{
pulic:
Test(int n){} //构造1
Test(int n,int m){} //构造2
Test(){} //构造3
}
Test array1[3] = {1Test(1,2)}; //三个元素分别于1,2,3构造初始化

Test array2[3] = {Test(2,3),Test(1,2),1}; //三个元素分别用2,2,1初始化

Test * Parray[3] = {new Test(4),new Test(1,2)}; // 1,2 Parray[2]没有被初始化!

复制构造函数

只有一个参数,即对同类对象的引用

形式:  
X::X(X &)

X::X(const X &) //可以常量对象作为参数

如果没有定义复制构造函函数,那么编译器生成默认复制构造函数。默认的复制构造函数完成复制功能。


  • 无参的构造函数也叫默认构造函数不一定存在,没有任何构造函数编译器就会生成无参构造函数,写了构造函数系统就不会生成无参构造函数

  • 复制构造函数一定存在,你不写编译器就帮你形成。

  • 构造函数不一定是无参的,有了构造函数,就不会自动生成无参构造函数了

//复制构造函数概念

class Complex{
private:
double real,imag;
};
Complex c1; //无参构造函数初始化
Complex c2(c1); //复制构造函数,将c2初始化成和c1一样,复制的工作

复制构造函数起作用的三种情况

  • 当用一个对象去初始化同类的另一个对象时。

    Complex c2(c1);

    Complex c2 = c1; //初始化语句,非赋值语句,与上面等价
  • 如果某函数有一个参数是类A的对象,那么该函数被调用时,类A的复制构造函数将被调用

    class A
    {
    public:
    A(){};
    A(A & a){
    cout <<"Copy constructor called"<<endl;
    //没有写复制的语句
    }
    };
    void Func(A a1){} //参数传递后有一个复制的工作
    int main()
    {
    A a2;
    Func(a2); //此时a1就是a2的复制品
    return 0;
    }

    //如果自己写了复制构造函数,但是里面没有写复制的语句,则上面就不能完成复制,则此时形参就不一定是实参的拷贝
    class A
    {
    public:
    A(){};
    A(A & a){
    cout <<"Copy constructor called"<<endl;
    //没有写复制的语句
    }
    };

  • 如果函数的返回值是类A的对象时,则函数返回时,A的复制构造函数被调用

    class A{
    public:
    int v;
    A(int n) { v = n; };
    A(const A & a){ //复制构造函数
    v = a.v;
    cout<<"Copy constructor called"<<endl;
    }
    };
    A Func(){ //是返回类型为对象的函数
    A b(4);
    return b; //这是复制构造函数的参数
    //在函数返回时,通过复制构造函数,使得返回值赋值为b.v
    }
    int main(){
    cout<<Func().v<<endl; //输出的是返回值对象的V,相当于b.v
    //Fun函数返回时,复制了b.v
    return 0;
    }

总结三种情况:用一个对象初始化另一个对象、函数的形参是个对象、函数的返回值是对象

注意:对象间的赋值并不导致复制构造函数被调用

class CMyclass {
public:
int n;
CMyclass(){};
CMyclass(CMyclass & c){n = 2 * c.n;}
};
int main(){
CMyclass c1,c2;
c1.n = 5; //
c2 = c1; //单纯的对象间的赋值操作不会导致复制构造函数被调用,类似结构体赋值
CMyclass c3(c1); //这里使用了复制构造函数,但是函数内做的不是复制的工作
cout <<"c2.n="<< c2.n <<",";
cout <<"c3.n="<< c3.n <<endl;
return 0;
}

常量引用参数

void fun(CMyclass obj_){
cout <<"fun"<<endl;
}

//这样的函数,调用时生成形参会引发复制构造函数调用,开销比较大。
//所以可以考虑使用CMyclass & 引用类型作为参数。
//如果希望确保实参的值在函数中不应被改变,那么可以加上const关键字:

void fun(const CMyclass & obj){
//在函数中任何试图改变obj值的语句都将变成非法
}

为什么要自己写构造函数????

类型转换构造函数

定义转换构造函数的目的是实现类型的自动转换

只有一个参数,而且不是复制构造函数的构造函数,一般就可以看作是转换构造函数。

当需要的时候,编译系统自动调用转换构造函数 ,建立一个无名的临时对象(或临时变量)。

class Complex{
public :
double real,imag;
Complex(int i){ //类型转换构造函数
cout << "IntConstructor called"<< endl;
real = i ; imag =0;
}
Complex(double r,double i){real = r; imag = i;}
};
int main(){
Complex c1(7,8);
Complex c2 = 12; //调用了Complex()构造函数,real = 12,imag = 0;
c1 = 9; // 9被自动转换成一个临时Complex对象,值为(9,0)然后进行赋值
cout << c1.real<<","<<c1.imag <<endl;
}

析构函数

名字与类名相同在前面加‘ ~’ ,没有参数和返回值 ,一个 最多只能有一个析构函数

析构函数对象消亡时象消亡时即自动被调用。用来在对象消亡前做善后工作,比如释放分配的空间 等。

如果定义类时没写析构函数,则编译器生成缺省析构函数 ,缺省析构函数什么也不做

如果定义了析构函数,则编译器就不在定义缺省的析构函数了

class String{
private :
char * p;
public:
String(){
p = new char[10];
}
~ String();
}
String ::~String(){
delete [] p; //释放内存
}

析构函数和数组

class Ctest{
public:
~Ctest(){ cout<<"destryctor called"<< endl;}
};
int main()
{
Ctest array[2]; //数组消亡,数组中每个元素依次调用消亡,故会输出两行destrytor 。。
cout <<"End Main"<<endl;
return 0;
}

析构函数和运算符delete

Ctest * pTest;
pTest = new Ctest; //构造函数调用
delete pTest; //析构函数调用
//---------------------------------
pTest = new Ctest[3]; //构造函数调用 3 次
delete [] pTest; //析构函数调用 3 次, 动态数组消亡的写法

//若new 一个对象数组,那么用delete释放时应该写[]。否则只delete一个对象(即调用依次析构函数)

析构函数在对象作为函数返回值返回后被调用

class CMyclass{
public :
~CMyclass(){cout <<"destructor"<<endl;}
};
CMyclass obj;
CMyclass fun(CMyclass sobj){ //参数对象消亡也会导致析构函数被调用
return sobj; //返回时,函数形参对象消亡,调用复制构造函数 1
}
int main(){
obj = fun(obj); //函数调用的返回值被调用过后,该临时对象析构函数被调用 2
return 0; //函数结束时,全体变量对象的析构函数被调用 3
}

构造函数和析构函数什么时候被调用

实例:

class Demo{
int id;
public:
Demo(int i){ //类型转换构造函数(只有一个参数的构造函数,)
id = i;
cout <<"id="<<id<<"constructed"<<endl;
}
~Demo(){
cout << "id="<<id<<"destructed"<<endl;
}
};

Demo dl(1); //全局对象初始化引发构造函数,会输出id 1 =constructed
void Fun();
void Func()
{
static Demo d2(2); // 静态局部变量,函数结束时不消亡,程序结束消亡
Demo d3(3); //定义d3类,函数结束时就消亡了
cout<<"func"<<endl;
}
int main(){
Demo d4(4); //局部对象定义引发构造函数,输出 id 4 =constructed
d4 = 6; //使用了的类型转换构造函数,创建了临时对象,id 6 =constructed
//结束后,临时对象消亡id 6 destructed;
cout<<"main"<<endl;
{
Demo d5(5); //定义了d5 ,生存期为{} id 5 =constructed
}//出了花括号,d5消亡,id 5 destructed;
Func();
cout<<"main ends"<<endl;
return 0;
} //先定义初始化,后析构,先d2 消亡,后d1消亡

//delete时析构函数同样被调用!

复制构造函数在不同编译器中的表现

class A{
public:
int x;
A(int x_):x(x_){
cout<<x<<"constructor called"<<endl;
}
A(const A&a){ //dev需要这里的const,其他编译器不要
x = 2 +a.x;
cout<<"copy called"<<endl;
}
~A(){
cout <<x<<"destructor called"<<endl;
}
};

Af(){
Ab(10);
return b;
}

int main(){
A a(1);
a = f();
return 0;
}

image-20210320182435205

类类型作函数参数的三种方式

c++依然采用传值的方式传递参数

对象本身作为参数

  • 使用对象本身作为参数时,形参时实参的一个拷贝,即调用了拷贝构造函数。最好自己定义一个明确的构造函数,以免发生不容易发现的错误。
  • 多次调用拷贝构造函数,效率低

对象引用作为参数

  • 推荐用这一种,容易理解使用,没有副作用,参考 引用 那里

对象指针作为参数

  • 使指针指向实参对象,还是有指针空间的使用和释放,空间相对较小

this指针

c++翻译到c

起初是没有c编译器的,是将c翻译到c

class CCar{
public:
int price;
void SetPrice(int p);
};
void CCar::SetPrice(int p)
{
price = p;
}
int main()
{
CCar car;
car.SetPrice(20000);
return 0;
}

翻译为C语言,如下:

sturct CCar {
int price;
};

void SetPricr(struct CCar * this,int p) //增加了this指针
{
this->pricr = p;
}
int main()
{
struct CCar car;
SetPrice(&car,20000);
return 0;
}

C++中的成员函数到了C中就会成为全局函数,并且在参数中多出一项this指针,这个this指针含义是指向被作用的对象

this指针的作用: 指向成员函数所作用的对象

非静态成员函数中可以直接使用this来代表指向该函数作用的对象的指针

class Complex{
public:
double real imag;
void Print(){
cout<< real<<","<<imag;
}
Complex(double r,double i):real(r),imag(i)
{ }
Complex AddOne(){
this->real ++; //等价于real++ ,this指向这个函数作用的对象
this->Print(); //等价于 Print()
return * this;
}
}

int main()
{
Complex c1(1,1),c2(0,0);
c2 =c1.AddOne();//c1 real + 1,并且输出了2,1
return 0;
}
//看着有点怪,但是正确的例子
class A
{
int i;
public:
void Hello(){
cout << "hello"<<endl;
}
}
int main()
{
A*p = NULL;
p->Hello(); //相当于hello(p)
}

class A
{
int i;
public:
void Hello(){
cout << i << "hello"<<endl;
//转化为,void Hello(A * this){cout<< this->i <<"hello"<<endl;}
}
int main()
{
A*p = NULL;
p->Hello(); //相当于hello(p)
}
//静态成员函数中不能使用this指针!
因为静态成员函数并不具体作用于某个对象
因此,静态成员函数的真实的参数的个数,就是程序中写出的参数个数

“ : ”在c++中的作用

对目标x 初始化

#include<iostream>

using namespace std;

class A{
public:
const int x = 100; //const 声明或定义的变量是不能被赋值的,但是在下面的语句中,可以通过初始化,来改变它的初始值
A(int x_):x(x_){ //: 后的x是指函数中的x,意思是用x_也可以是数字,将x初始化
cout<<x<<"constructor called"<<endl;
}
};
const int abc = 0;
A f(){
A b(10);
return b;
}

int main(){
A a(10);
cout << a.x << endl;
return 0;
}

在初始化列表中是对变量进行初始化,而在构造函数内是进行赋值操作。

两都的差别在对于像const类型数据的操作上表现得尤为明显。我们知道,const类型的变量必须在定义时进行初始化,而不能对const型的变量进行赋值,因此const类型的成员变量只能(而且必须)在初始化列表中进行初始化

其他参考文章:https://blog.csdn.net/chw1989/article/details/7480375

静态成员变量

静态成员

在定义前面加了 static 关键字的成员,本质是全局变量

class CRectengle
{
private:
int w ,h;
static int nTotalArea; //静态成员变量
static int nTotalNumber;
public:
CRectangle(int w_,int h_);
~CRectangle();
static void PrintTotal(); //静态成员函数
}

静态的成员变量,是所有该类的对象共享的,而普通变量是只有对象自己能用。

// 另  sizeof 运算符不会计算静态成员变量,因为静态变量实际上不是放在对象内部的,而是在整个对象的外部

如:
class CMclass{
int n;
static int s;
};
sizeof(CMyclass); //结果为4 ,而不是8


  • 普通成员函数

    必须作用于一个对象,通过对象来调用

  • 静态成员函数

    并不具体作用于某个对象,因此不需要通过对象就能访问

//如何访问静态成员变量、函数?

//1 类名::成员名
CRectangle::PrintTotal();
//2 对象名.成员名
CRrctangle r;
r.PrintTotal();
//3 指针->成员名
CRectangle*p = &r;
p->Printotal();
//4 引用.成员名
CRectangle &ref = r;
int n = ref.nTotalNumber;

  • 引入的意义,把和类紧密相关的内容能写到类中去,使得成为一个整体,便于理解维护。

比如:记录与类相关的数据,就可以用静态变量,放到类里

//实例

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 CRectangle::PrintTotal()
{
cout<<nTotalNumber<<","<<nTotalArea<<endl;
}

//特别注意 静态函数必须在定义类的文件中对静态成员变量进行依次说明或初始化,否则编译能通过,链接不能通过

int CRectangle::nTotalNumber=0; //初始化,可以赋初值

//静态成员函数中,不能访问非静态成员变量,也不能第调用用非静态成员函数
void CRectabgle::PrintTotal()
{
cout <<w<<","<<.... //wrong
}

//非静态函数,里面会调用非静态变量,所以也是不可以的
  • 缺陷

    复制构造函数生成对象初始化,就导致会存在漏洞

  • 临时对象消亡时调用析构函数,影响数值

  • 要自己写一个复制构造函数,包含对静态变量的操作

静态变量和常量

共同点

  • static和const在编译时直接分配内存。

区别

  • (本质)static是类型引用,const是实例引用。
  • (赋值)静态变量的值在运行时可以更改赋值,而常量的值是不可改变的,运行一开始已经固定,之后修改会报错。
  • (内存)静态变量存放在全局数据区中,伴随着这个程序流程,能将此变量的值保留到下次调用,不过数据过大的静态变量有可能造成内存泄露。 而const常量算是一个普通的只读变量,随函数结束而结束。在C里,const常量总是会分配内存,位于只读数据段。在C++,如果const常量在没有声明为extern,那么就是一个编译时的符号,不占用内存。

成员对象和封闭类(enclosing)

class CTyre  //l轮胎类
{
private:
int radius; //半径
int width; //宽度
public:
CTyre(int r,int w):radius(r),width(w){} //需要参数
};

class CEngine //引擎类
{

};

class CCar //这个类就是封闭类
{
private:
int price;
CTyre tyre; //成员对象 ,构造时如果需要参数,调用该类时需要给出参数
CEngine engine; //成员对象
public:
CCar(int p,int tr,int tw){};
};
CCar::CCar(int p,int tr,int w):price(p),tyre(tr,w)
{

};

int main()
{
CCar car(20000.17,225);
return 0;
}
  • 封闭类构造函数和析构函数的执行顺序
    • 封闭类对象生成时,先执行所有对象成员的构造函数,然后执行封闭类的构造函数。
    • 对象成员的构造函数调用次序和对象成员在类中的说明次序一致,与它们在成员初始化列表中出现的次序无关。
    • 当封闭类的对象消亡时,先执行封闭类的析构函数,然后再执行成员对象的析构函数。次序和构造函数的调用次序相反。

先构造的后析构,先初始化后消亡

//实例
class CTyre
{
public:
CTyre(){cout << "CTyre contructor" <<endl;}
~CTyre(){cout << "CTyre destructor" <<endl;}
};
class CEngine
{
public:
CEngine(){cout << "CEngine contructor" <<endl;}
~CEngine(){cout << "CEngine destructor" <<endl;}
};
class CCar
{
private:
CEngine engine;
CTyre tyre;
public:
CCar(){cout<<"CCar contructor"<<endl;}
~CCar(){cout << "CCar destructor" <<endl;}
}

int main()
{
CCar car;
return 0;
}
  • 封闭类的复制构造函数

    class A
    {
    public:
    A(){cout <<"default"<<endl;}
    A(A & a){cout <<"copy"<<enld;}
    };
    class B(A a;);

    int main()
    {
    B b1,b2(b1);
    return 0;
    }

常量对象、常量成员、常引用

  • 如果不希望改变某个对象的值,即可以再定义对象前加const关键字

    class Demo
    {
    private:
    int value;
    public:
    void SetValue(){}
    };
    const Demo Obj; //常量对象
  • 在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数

  • 常量成员函数执行期间不应修改其所作用的对象 。因此,在常量成员函数中不能修改成员变量的值( 静态成员变量除外),也不能调用同类的非常量成员函数 (静态成员函数除外)

    class Sample
    {
    public
    int value;
    void GetValue() const;
    void func(){};
    Sample(){}
    };
    void Sample::GetValue() const
    {
    value = 0;//wrong 常量函数内部,不能调用或改变非常量成员
    func();//wrong 常量函数内部,不能调用或改变非常量成员函数
    }

    int main()
    {
    const Sample o;
    o.value = 100; //err.常量对象不可被修改
    o.func(); //err.常量对象上面不能执行非常量成员函数
    o.getvalue(); //ok. 常量对象上可以执行常量成员函数
    return 0;
    }
    //在dev c++中 要为Sample类编写无参构造函数才可以。

常量成员函数的重载

class CTest
{
private:
int n;
public:
CTest(){n=1;}
int GetValue() const {return n;}
int GetValue() {return 2*n;} //这里重载了上个函数
};

int main()
{
const CTest objTest1;
CTest objTest2;
cout << objTest1.GetValue()<<","<<objTest2.GetValue();
return 0;
}

常引用

class Sample
{
...
};
//函数参数调用时避免重复多次使用构造函数,提升效率,使用引用
void PrintfObj(const Sample & o) //因为有const,所以不能修改o值
{
.....
}

友元

  • 友元

    • 友元函数

    • 友元类

友元函数

一个类的友元函数可以访问该类的私有成员

友元函数并不是成员函数,友元函数可以是一个全局函数,当把这个全局函数声明为友元的时候,就可以访问该类的私有成员了


class CCar; //这里起提前声明的作用,以便CDriver类使用
class CDriver
{
public:
void ModifyCar(CCar *pCar); //改装车,如果这里不是一个指针而是一个类,则需要把CCar在上面写完整
};
class CCar
{
private:
int price;
friend int MostExpensiveCar(CCar cars[],int total); //声明友元,并不是成员函数
friend void CDriver::ModifyCay(CCAr * pCar); //声明友元,不是成员函数,而是上一个类的成员函数,这里把这个函数声明为CCar的友元
}
void CDriver::ModifyCar(CCar * pCar) //全局函数
{
pCar->price += 1000; //这里访问了类的私有成员
}

int MostExpensiveCar(CCar car[],int total) //全局函数
{
int tmpMax = -1;
for(int i = 0;i<total; ++i)
{
if(cars[i].pricr > tmpMax)
{
tmpMax = cars[i].price; //访问量私有成员
}
}
return tempMax;
}

int main()
{
return 0;
}
//可以将一个类的成员函数(包括构造、析构函数)说明为另一个类的友元
class B
{
public:
void function();
};

class A
{
private:
int num;
friend void B::function(); //这里使 类B 的成员函数成为 类A 的友元,这样 类B 的成员函数function就可以访问 类A 的私有成员 num了
};

友元类

如果A是B的友元,那么A的成员函数可以访问B的私有成员

class CCar
{
private:
int price;
friend class CDriver;
};

class CDriver
{
public
CCar myCar;
void ModifyCar(){ //改装汽车
myCar.price += 1000; //因CDriver是CCar的友元类,
//故此处可以访问其私有成员
}
};
int main(){return 0;}
//友元类之间的关系不能传递,不能继承