C++类, since 2020-11-27

(2020.11.27 Fri)
类Class,是具有相似内部状态和行为的实体的集合。类=数据+操作。

从结构体到类

结构体struct一般是各种变量的集合,其中也可以加入成员函数。

#include 
struct point
{
    int x;
    int y;
    void point()
    {
        cout <<"x="<
# 在类的外部定义构造函数
class complex
{
private:
    double real, imag;
public:
    complex(double r, double i);
    void disp()
    {
        cout<

实际使用中,如果没有给类定义构造函数,编译系统会自动生成一个默认的构造函数,这个默认的构造函数不带任何参数,只能给对象开辟一个存储空间,而不能为对象中的数据成员赋初值。此时数据成员的值是随机的,运行时会出错。系统自动生成的构造函数形式为

类名::构造函数名()
{
}
//比如
complex::complex()
{
}
构造函数的调用

定义对象的同时调用构造函数,调用格式为

类名 对象名(实参表)

构造函数一般不需要用户显示调用,声明对象时系统会自动调用构造函数。

class complex
{
private:
    double real, imag;
public:
    complex(double r, double i);
    void disp()
    {
        cout<

在这个例子中,没有直接调用构造函数complex(),只是声明了对象op,系统自动调用了构造函数,并完成了初始化工作。

不带参数的构造函数
#include 
class myclass
{
private:
    int a;
public:
    myclass();
    void disp()
    {
        cout<<"a*a="<

返回结果: initialised, a*a = 100。

带参数的构造函数

构造函数在声明时有默认参数 ,但是定义是不写这些参数。

#include 
#include 
class complex
{
    double real, imag;
public:
    complex(double r=0.0, double i=0.0); //声明时需要标明默认值
    double abscomplex();
};
complex::complex(double r, double i); //定义时不需要标默认值,但重载时可以定义时指定默认值
{
    real = r;
    imag = i;
}
complex::abscomplex()
{
    double  n;
    n = real*real + imag*imag;
    return sqrt(n);
}
void main() 
{ 
    complex op(); //调用默认参数
    complex op2(1.5, 3.0);  //调用赋值参数
    cout<<"op: "<

注意,构造函数的默认值一般在声明时给出,但在重载时可以在定义时给出。

构造函数重载

一个类中可以有多个不同参数形式的构造函数,用类去定义一个变量(后面可以附带参数),也就是内存中产生一个类的实例(用类定义的实例变量通常也叫对象)时,程序将根据参数自动调用该类中对应的构造函数。
作用
C++可以定义多个参数及参数类型不同的构造函数,用以适应不同的情况,增加程序灵活性,这些构造函数之间通过参数的个数或类型来区分。

#include 
class point
{
    double fx,fy;
public:
    point();
    point(double x, double y);
    void showpoint();
};
point::point()
{
    fx = 0;
    fy = 0;
}
point::point(double x, double y = 5.5)
{
    fx = x;
    fy = y;
}
void point::showpoint()
{
    cout<

拷贝构造函数

拷贝构造函数的作用是用一个已经存在的对象来初始化该类的新对象,用户可以根据需要定义拷贝构造函数,也可由系统生成一个默认的拷贝构造函数。格式如下

类名 (类名 &对象名)
{
    拷贝构造函数的函数体;
}
#include 
class point
{
    double fx,fy;
public:
    point(point &p);
    point(double, double);
    void showpoint();
};
point::point(point &p)
{
    fx = p.fx+10;
    fy = p.fy+20;
}
point::point(double x,double y)
{
    fx = x;
    fy = y;
}
void point::showpoint()
{
    cout<

在创建对象时,调用的是构造函数还是拷贝构造函数,由编译系统根据需要创建对的参数来确定。

调用拷贝构造函数

三种情况下拷贝构造函数会被调用

  • 用类的对象去初始化该类的另一个对象时
  • 函数的形参是类的对象,调用函数进行形参和实参的结合时
  • 函数的返回值是类的对象,函数执行完返回调用者时
#include 
class point
{
    int x,y;
public:
    point(int a=0,int b =0)
    {
        x = a;
        y = b;
    }
    point(point &p);
    int getx()
    {
        return x;
    }
    int gety()
    {
        return y;
    }
};
point::point(point &p)
{
    x = p.x+10;
    y = p.y+20;
}
void f(point p)
{
    cout<
默认拷贝构造函数

当用一个已经存在的对象初始化本类的新对象时,如果没有自定义拷贝构造函数,则系统会自动生成一个默认的拷贝构造函数来完成初始化的工作。

#include 
class point
{
    int x,y;
public:
    point(int a,int b)
    {
        x = a;
        y = b;
    }
    void showpoint();
};
void main()
{
    point p1(1.1, 2.2);
    cout<

默认的构造函数的调用也是由编译系统根据对象的特征自动调用的。

析构函数destructor

也是一种特殊的成员函数,被声明为公有成员。作用是释放分配给对象的内存空间,并做一些善后。

  • 名字必须与类名相同,但在名字前面加符号~
  • 析构函数没有参数,没有返回值,不能重载,在一个类中只能有一个析构函数
  • 撤销对象时,系统会自动调用析构函数完成空间的释放和善后
#include 
#include 
class complex
{
    double real, imag;
public:
    complex(double r=0.0, double i=0.0); 
    ~complex();
    double abscomplex();
};
complex::complex(double r, double i);
{
    cout<<"constructing..."<

返回的结果

constructing...
...
destructing...
  • 每个类都必须有一个析构函数,若没有显式的定义,系统会自动生成一个默认的析构函数,它是一个空函数
  • 对大多数类,默认的析构函数能满足要求,但如果对象在完成操作前需要做内部处理,则应显式的定义析构函数
  • 构造函数和析构函数的常见用法是,在构造函数中用new运算符为对象分配空间,在析构函数中用delete运算符释放空间

友元friend

为了使类的private and protected成员能够被其他类或其他成员函数访问,引入友元概念。友元提供了不同类或对象的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。如果友元是一般成员函数或类的成员函数,则成为友元函数;如果友元是一个类,则称为一个类,友元类的所有成员函数都是友元函数。

友元函数

与普通成员函数不同,不是当前类的成员函数,而是独立于当前类的外部函数;可以是普通函数或其他类的成员函数。定义友元函数后可以访问该类的所有对象的成员,包括private/protected/public。

friend函数使用前必须要在类定义时声明,声明时在其函数名前加上关键字friend。该声明可放在公有成员中,也可放在私有成员中。定义既可以在类的内部进行,也可以在类外部进行,但通常定义在类的外部。一般声明格式

friend <数据类型><友元函数名>(参数表); 
#include 
class point
{
    int x,y;
public:
    point(int a=0,int b =0)
    {
        x = a;
        y = b;
    }
    point(point &p);
    int getx()
    {
        return x;
    }
    int gety()
    {
        return y;
    }
    friend double dist(point &p1, point &p2); //声明友元函数
};
double dist(point &p1, point &p2) //在类的外部定义友元函数
{
    return (sqrt((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y))); //友元函数可访问私有成员
}
point::point(point &p)
{
    x = p.x+10;
    y = p.y+20;
}

void main()
{
    point p1(2,4);
    point p2(4,5);
    cout<
  • 由于友元函数不是成员函数,因此在类的外部定义友元函数时,不必像成员函数那样在函数名前加::
  • 友元函数不是类的成员,不能直接引用对象成员的名字,也不能通过this指针引用对象的成员,必须通过作为入口参数传递进来的对象名或对象指针来引用该对象的成员。为此,友元函数一般都带有一个该类的入口参数,如上面的double dist(point &p1, point &p2)
  • 当一个函数需要访问多个类时,应该把这个函数同时定义为这些类的友元函数,使其能访问这些类的数据。
友元成员

如果一个类的成员函数是另一个类的友元函数,则这个成员函数是友元成员,它不仅可以访问自身类的私有公有成员,也可以访问声明了friend的类中的私有公有成员。

#include 
#include 
class boy;
class girl
{
    char *name;
    int age;
public:
    girl(char *n, int a)
    {
        name =new char[strlen(n)+1]; // 分配空间
        strcpy(name,n); //调用字符串拷贝函数
        age= a;
    }
    void ptr(boy &); //声明公有成员
    ~girl();
    {
        delete name;
    }
};
class boy
{
    char *name;
    int age;
public:
    boy(char *n, int a)
    {
        name =new char[strlen(n)+1]; // 分配空间
        strcpy(name,n); //调用字符串拷贝函数
        age= a;
    }
    friend void girl::prt(boy &); //声明友元函数
    ~boy();
    {
        delete name;
    }
}; 
void girl::prt(boy &b)
{
    cout<<'girls age'<

需要注意,当一个类的成员函数作为另一个类的友元函数时,必须先定义成员函数所在的类,如上所示,先定义了类girl。在声明友元函数时,要加上成员函数所在类的类名和运算符::。类定义前使用该类的成员,需要在使用前对该类进行声明,比如上面声明了class boy,否则系统报错。

友元类

一个类左右另一个类的友元,称为友元类。成为了友元类,则这个类的所有成员含糊都成为另一个类的友元函数,因此一个类都可以通过对象名直接访问另一个类中的私有成员。友元类的声明可以放在类声明中的任何位置,此时友元类中的所有成员函数都是友元函数。一般格式如下

friend class <友元类名> //第一种格式
friend <友元类名> //第二种
#include 
#include 
class boy;
class girl
{
    char *name;
    int age;
public:
    girl(char *n, int a)
    {
        name =new char[strlen(n)+1]; // 分配空间
        strcpy(name,n); //调用字符串拷贝函数
        age= a;
    }
    void ptr(boy &); //声明公有成员
    ~girl();
    {
        delete name;
    }
};
class boy
{
    char *name;
    int age;
    friend girl;  //声明一个友元类
public:
    boy(char *n, int a)
    {
        name =new char[strlen(n)+1]; 
        strcpy(name,n); 
        age= a;
    }
    friend void girl::prt(boy &); //声明友元函数
    ~boy();
    {
        delete name;
    }
}; 
void girl::prt(boy &b)
{
    cout<<'girls age'<

友元关系是不能传递的,类B是类A的友元,类C是类B的友元,则C和A之间,除非特别声明,没有任何关系,不能共享数据。友元关系是单向的。B是A的友元,B可以访问A的私有和保护成员,反之,A的成员不能访问B的私有和保护成员。

Reference

1 刘蕾编著,21天学通C++(第五版),电子工业出版社
2 聚慕课教育研发中心 编著,C++从入门到项目实践(超值版),清华大学出版社

你可能感兴趣的:(C++类, since 2020-11-27)