将一个类作为另一个类的成员: 类对象和类指针

1.类对象和类指针的区别

1.1 类对象和类指针的定义

class Data
{
public:
    Data(){cout << "Data::Data()" << endl;}
    ~Data(){}
    void setData(int data) { cout << "Data::setData" << endl; m_iData = data; }
    int getData() { return m_iData; }
private:
    int m_iData;
};


Data a; //实例化一个类对象a
a.setData(1);
 
// Data *b = new Data();
Data *b; //实例化一个类指针b
b =  new Data();
b->setData(2);

1.2 区别

(1)内存:
类对象: Data a;在定义之后就已经为a这个对象分配了内存,且为内存栈,是个局部的临时变量;
类指针: Data *b = new Data();在定义*b的时候并没有分配内存,只有执行new后才会分配内存,且为内存堆,是个永久变量,除非你释放它。

(2)引用成员: 
类对象: 用"."操作符
类指针: 用"->"操作符

(3)多态:
类指针: 是间接访问,但可实现多态(通过父类指针可调用子类对象),并且没有调用构造函数。
类对象: 可直接访问,但不能实现多态,声明即调用了构造函数(已分配了内存)。
/*------------------------------------------------------------------------------*/
class Data
{
public:
    Data(){ cout << "Data::Data()" << endl; }
    ~Data(){ cout << "Data::~Data()" << endl; }
    void setData(int data) { cout << "Data::setData" << endl; m_iData = data; }
    int getData() { return m_iData; }
private:
    int m_iData;
};

class CData : public Data
{
public:
    CData(){ cout << "CData::CData()" << endl; }
    ~CData(){ cout << "CData::~CData()" << endl; }
    void setData(int data) { cout << "CData::setData" << endl; m_iData = data; }
    int getData() { return m_iData; }
private:
    int m_iData;
};

int main()
{
    /*CData d1;
    调用了构造函数
        Data::Data()
        CData::CData()
    调用了析构函数
        CData::~CData()
        Data::~Data()
    */
    CData d1;
    Data * d2 = &d1; // 通过父类指针可调用子类对象,没有调用构造函数和析构函数
    return 0;
}
/*------------------------------------------------------------------------------*/
类指针可以指向多个不同的对象,这就是多态

1.3 什么情况使用类对象与类指针?

作用基本一样 都是为了调用类的成员变量和成员函数;
当你希望明确使用这个类的时候,最好使用对象;
如果你希望使用C++中的动态绑定,则最好使用指针或者引用,指针和引用用起来更灵活,容易实现多态等。

1.4 类对象和类指针联系

在类的声明尚未完成的情况下,可以声明指向该类的指针,但是不可声明该类的对象;
父类的指针可以指向子类的对象。

1.5 指针与虚函数

要发挥虚函数的强大作用,必须使用指针来访问对象。

当类是有虚函数的基类,Func是它的一个虚函数,则调用Func时:
        类对象:调用的是它自己的Func;
        类指针:调用的是分配给它空间时那个类的Func。

1.6 指针好处

1. 实现多态。
2. 在函数调用,传指针参数。不管你的对象或结构参数多么庞大,你用指针,传过去的就是4个字节。如果用对象,参数传递占用的资源就太大了。

2.类对象和类指针作为另一个类的成员

类对象:
1. 首先,要将A类的对象作为B类的成员,你必须在B类声明前声明A类。这样,在B类声明中,就知道A类是一个类,不然编译器是不知道A这个字母代表什么,只有在B类声明前声明了A类,
B类中就知道A代表的是一个类,是一种自定义类型。要达到这个效果,需要在B类声明前,包含A类的头文件,A类的头文件中就是A类的声明,有A类的数据成员,也有成员函数等。
2. 其次,就是像声明一个成员变量一样,在B类中添加一个成员,如A m_a; 这句表示使用A类声明了一个B类的成员变量m_a。但是在此,只是对m_a成员变量声明而已,并没有分配内存空间,
具体的声明定义和初始化等,请阅读《C/C++声明定义初始化和赋值独家剖析深刻理解》。
3. 最后,初始化m_a。因为这个成员是一个类对象,因此需要调用构造函数才能初始化。因为这个对象m_a在B类中,m_a对象的构造必然是在B类对象构造之前进行,因此不能在B类构造函数中进行,
又因为m_a不是全局对象,也不能在函数外部。如何解决这个问题呢?这就是C++提出的成员初始化列表,用来解决这类问题的。如果A类提供了默认构造函数,不需要参数的,那就不需要显式初始化,
让编译器自动完成即可,但是有时候需要在构造时就需要传值,此时就只能通过成员初始化列表。
/*-------------------------------------初始化例子------------------------------------------*/
class Data
{
public:
    Data(int data): m_iData(data){ cout << "Data::Data()" << endl; }
    ~Data(){ cout << "Data::~Data()" << endl; }
    void setData(int data) { cout << "Data::setData" << endl; m_iData = data; }
    int getData() { return m_iData; }
private:
    int m_iData;
};

class TData
{
public:
    TData(): m_cData(5){ cout << "TData::TData()" << endl; }
    ~TData(){ cout << "TData::~TData()" << endl; }
    void printData() { cout << "data = " << m_cData.getData() << endl; }
private:
    Data m_cData;
};


int main()
{
    TData d1;
    d1.printData();
    return 0;
}
结果:
Data::Data()
TData::TData()
data = 5
TData::~TData()
Data::~Data()
注意:
如果Data类的构造函数是无参的,那么在TData类的构造函数初始化列表中无需初始化(不需要显式初始化)。
/*-------------------------------------初始化例子------------------------------------------*/

类指针:
这种方法比较便捷,省去了初始化的麻烦,且可以动态的创建对象,但是也会带来内存的使用问题。初学者推荐使用第一种方法。指针的使用,需要一定功底,如果使用不当,
会出现内存访问违规或者内存泄露问题。指针的深入理解,请参考《指针的深入理解,深刻掌握指针》。

1. 首先,在B类声明前,包含A类头文件,这个第一个方法的是一样的解释,即使是声明指针,也要包含,因为要使用A代表一个类,就必须先包含A类头文件告诉编译器A是一个类的代名词而不是简单的字符。
2. 其次,声明A类指针作为成员,如A * pA;这样就可以声明了。很简单,这个指针和普通指针一样大,并不占用很多内存。在很多要动态创建很多对象时特别方便。用完就释放,需要就创建。
3. 然后,就是初始化指针。在B类构造函数中,初始化时将这个指针设为NULL。这是规范的写法,之后创建对象后便指向了对象,此时指针就不是NULL了,删除指针指向的对象后,一定要将指针值设置成NULL。
这样,要判断指针是否指向一个对象,就只要判断指针是否为NULL即可。
4. 最后,在使用时,需要A类对象时,new一个A类对象,然后赋值给pA,这样pA就指向了new出来的对象,然后都用指针来操作A。用完后,使用delete pA 。

特别说明:为了防止内存泄露,new了对象后,一定要delete这个对象。最容易出现内存泄露的就是频繁的new对象和delete对象,导致前一个指向的对象没有删除就new了一个新对象给指针,
最后之前的对象就无法使用,知道程序结束才能被释放,这就是内存泄露了。正确的代码写法如下:
-----------------代码-------------------------------
if(NULL != pA)//此处需要初始化时设置指针为空。
{
  delete pA;
  pA = new A;
}  else {
  pA = new A;
}
-----------------代码-------------------------------

参考:
http://www.voidcn.com/article/p-qdhfbeaz-bmz.html
https://blog.csdn.net/keneyr/article/details/89364275
https://blog.csdn.net/liang841451955/article/details/80266744

posted on 2021-05-26 13:09  JJ_S  阅读(2774)  评论(0编辑  收藏  举报