C++ primer Plus 第十章:对象和类

10.2.2 C++中的类

类是一种将抽象转换未用户定义类型的C++工具,它将数据表示和操作数据的方法合成一个整洁的包。

接口:一个共享框架,供两个系统交互时使用。

1.访问控制

使用类对象的程序可以直接访问类的公有部分,但只能通过公有成员函数访问对象的私有成员

防止程序直接访问数据被称为数据隐藏。

数据隐藏不仅可以防止直接访问数据,而且还让开发者无需了解数据是如何被表示的。

将实现细节放在一起并将它们与抽象分开称为封装

2.private和public

在类中使用private来强调成员是私有的,public强调成员是公有的。

在class声明的类中,成员默认是私有的,而在struct中成员默认是公有的。

10.2.3实现类的成员函数

定义成员函数时,使用作用域解析运算符::来来标识函数所属的类

类方法可以访问类的private组件

如stock是一个类名,它有一个叫fun的成员函数

void stock::fun()

{

。。。。

}

1.内联函数

定义在类声明中的函数都将自动成为内联函数,类声明常将短小的成员函数作为内联函数,如果想在类外声明内联函数,可以使用inline关键字。

内联函数要求每个使用它们的文件中都对其进行定义,为了方便使用内联函数,我们将内联函数放在类的头文件中。

2.方法使用哪个对象

创建对象: 类名 对象名(类名相当于一种数据类型 对象名是变量)

使用成员函数方法 对象名.函数名(参数)

10.3类的构造函数和析构函数

对象的变量不能像初始化int变量 那样来进行初始化,因为对象的存在私有成员,在类外无法进行访问。而类的成员函数可以访问这些成员变量,因此我们可以使用类的成员函数对对象进行初始化,而类的构造函数就是C++用来初始化对象的一个函数。

构造函数在创建对象的时候会被自动调用。

构造函数:

名字必须与类名一致,且没有返回类型,可以有参数,可以重载(也就是可以有多个构造函数)。如fun类的构造函数为 fun();

若不定义构造函数,程序将提供一个默认的隐藏的构造函数,而这个构造函数并不进行任何操作。若手动定义了多个构造函数,

程序则会根据定义对象时候括号里的参数,来确定使用哪一个构造函数。

如果提供了非默认的构造函数,下列语句会出错

fun A;定义一个fun类的对象A 

有两种方法可以解决这个问题,一是在类里加入一个默认的构造函数,二是给非默认的构造函数提供默认参数。

但是这两种方法不能同时用,如果同时用的话,fun A;可以使用任意一个构造函数,于是程序将不知道使用哪一个,会爆错。

列表初始化

听名字就是列一个表进行初始化

语法就是下面这种

#include <iostream>

using namespace std;
class A
{
    int w;
public:
    A(int j=0):w(j){}
};
int main()
{
    A a;
    return 0;
}

在函数名后加一个分号然后写出变量名 ,变量名后跟一个括号,意思是用括号里这个值来初始化这个变量

注意事项:

这种格式只能用于构造函数

必须用这种格式来初始化非静态const数据成员和引用数据成员

什么时候初始化列表是必用的呢?

1. 类成员为const类型

2. 类成员为引用类型

#include <iostream>
using namespace std;

class A
{
    public:
        A(int &v) : i(v), p(v), j(v) {}
        void print_val() { cout << "hello:" << i << "  " << j << endl;}
    private:
        const int i;
        int p;
        int &j;
};

int main()
{
    int pp = 45;
    A b(pp);
    b.print_val();
}

构造函数 所说是起到初始化的作用,但是实际上是通过赋值的操作来进行初始化的,但是const变量 和引用无法通过赋值来进行初始化,所以要用初始化列表。

3. 类成员为没有默认构造函数的类类型

#include <iostream>
using namespace std;

class Base
{
    public:
        Base(int a) : val(a) {}
    private:
        int val;
};

class A
{
    public:
        A(int v) : p(v),b(v) {}
        void print_val() { cout << "hello:" << p << endl;}
    private:
        int p;
        Base b;
};

int main()
{
    int pp = 45;
    A b(pp);
    b.print_val();
}

原因同样是创建对象时,要初始类成员的每一个成员

(如果没有在初始化列表里面,编译器会自动使用它的默认的构造函数进行初始化,但是它没有默认构造函数,所以会编译报错,所以没有默认构造函数的成员变量需要使用初始化列表进行初始化)

这里如果把初始化列表里的b删掉,就会爆错,同理类存在继承关系,派生类必须在初始化列表里初始化。

4. 如果类存在继承关系,派生类必须在其初始化列表中调用基类的构造函数

析构函数

析构函数只能有一个,名字与类名相同,定义时候需要在前面加上~,没有参数,

对应于构造函数初始化对象,析构函数是用来释放对象的内存的,可以将delete等操作放在析构函数里,析构函数在对象离开其作用域的时候自动执行。

对象析构的顺序和创建的顺序相反

使用定位new 分配内存的对象,无法自动执行析构函数,这种情况需要显式的调用析构函数。

10.4this指针

众所周知,同一个类的多个对象公用一套方法,那么在调用同一套方法的时候,程序是如何知道要访问哪个对象里的成员的呢,答案是通过this指针,类的成员函数里有一个隐藏的指向对象的参数就是this指针。

具体看下面这个例子

#include <iostream>
using namespace std;

class Base
{
    public:
        Base(int a) : val(a) {}
        void display();
    private:
        int val;
};
void Base::display()
{
    cout<<val<<endl;//实际上就是cout<<this->val<<endl;
}
int main()
{
   Base a(1),b(2);
   a.display();//看上去没有传参数,实际上传了a的地址
}

 

10.5对象数组

对象数组跟结构体差不多,这里就讲讲一些不同点。

首先是初始化,由于对象有构造函数,所以我们在初始化对象数组的时候要用大括号括起来,将构造函数列出来,来进行初始化

例如

const int max=4;
fun a[max]={ fun(1),fun(2),fun(3),fun(5)}
如果类只有一个成员变量,则可以直接像数组那样赋值
如:fun b[max]={1,2,3,4};

 

发布了37 篇原创文章 · 获赞 3 · 访问量 2380

猜你喜欢

转载自blog.csdn.net/Stillboring/article/details/105230931