C++中类和对象以及成员函数

一个实体拥有自己的属性和行为,属性是私有的,行为是共有的,在C++中实体对应的就是对象,实体抽象数据类型就是类,属性是成员变量,行为是成员函数。

面向对象思想的三大特征:

封装、继承、多态(静多态,动多态)

访问限定符:

public:任意位置都可以访问。

protected:(继承和派生)一子类可以访问父类的保护成员。

private:只有在类中才能被访问。

如果在class类中没有说明是public还是private,则默认是private的。

类和结构体都是自定义类型,那么他两之间有什么区别呢?

在C++中,结构体默认是全部都可见的,而类中默认是私有的。

C++的class和C语言的结构体的区别:

①struct Node是一个类型,在C语言中这样定义struct Node a;定义了一个结构体类型的a变量;在C++中,结构体定义对象可以写成Node a。

②空的结构体在C语言编译通不过,会报错;在C++中大小为1;不过在Linux环境里大小则为0;空的类(C++)大小也为1。

这时就会有一个疑问??空类大小为何是1而不是0呢??

空类的实例中不包含任何信息,本来求sizeof应该是0,但是当我们声明该类型的实例的时候,它必须在内存中占有一定的空间,否则无法使用这些实例。至于占多少内存,由编译器决定。VS中是1。

我们定义一个类,里面都会实现一些函数,那么我们在进行函数设计的时候应该注意下面这问题:

下面这两个函数有什么区别呢?

    void GetName(char *name);//1

    char *GetName();//2

函数1引进了一个附本,修改的附本,并不会影响成员变量原本的值;

函数2返回值是成员变量的指针,这样就有可能会修改私有成员变量的值。

所以我们在进行函数设计的时候,应尽量避免返回成员变量的指针,修改到成员变量的值导致程序bug。

成员函数我们可以将它们定义在类里,也可以定义在类外,一般建议定义在类外,在类里进行声明即可,这样会使代码看上去更美观,类里代码不会过多,看上去头重脚轻。定义在类外格式如下:

    void Student::Show();   //Student是类名,::是作用于分辨符,这个是一定要有的,Show()就是成员函数。

那么类中定义和类外定义有什么区别呢??

①如果在类中定义,在函数调用的时候编译器会当做内联函数处理,在调用点将代码展开;

②如果在类外定义,在函数调用时和普通函数一样,进行栈桢的开辟和回退。

------------------------------------------------------------------------------------------------------------------------

系统会给我们提供一些函数,它们都有两个特点:公用的,内联的(无堆栈的开辟和清理)

接下来我们主要讲一下几个成员函数:

现在我们有一个CGoods类,私有成员变量有:物品名称(_name),数量(_amount),价格(_price)

1、构造函数(初始化对对象的内存地址空间)

构造函数和类名相同,构造函数可以重载。

系统提供的默认构造函数:

CGoods(){}

自己提供的构造函数:

CGoods(char *name,int amount,int price)

{

         this->_name=newchar[strlen(name)+1];

         strcpy(_name,name);

         this->_amount=amount;

         this->_price=price;

}

this指针的类型是:CGoods *constthis,this指针的指向不能改变。

实例化一个对象:

①分配对象内存空间

②调用对象的构造函数

int main()

{

         CGoodsgood1;//1

         CGoodsgood2("good2",20,10.5);//2

}

对象1会调用系统给出的默认构造函数,对象2会调用自己写的构造函数。

没有实现的构造函数系统会给出默认的构造函数,如果有系统就不会调用默认构造函数,实例化一个不带参的对象不用加括号,比如对象1,带了括号就是函数的意思。

2、析构函数(释放对象的内存空间所占的资源)

析构函数不可以重载也不可以传参。调用的时机是在return 0;后在}前,先构造的对象后析构。

系统提供的:

~CGood(){}

自己实现的:

~CGoods()

{

         delete[]_name;

         _name=NULL;

}

_name是动态开辟的所以需要销毁否则会造成内存泄漏。
3、拷贝构造函数(用一个已经存在的对象初始化相同类型的新对象时,就会调用拷贝构造函数)

系统提供的:

CGoods(const CGoods &rhs)

{

         this->_name=rhs._name;

         this->_amount=rhs._amount;

         this->_price=rhs._price;

}

int main()

{

         CGoodsgood1;//1

         CGoodsgood2("good2",20,10.5);//2

         CGoodsgood3=goods2;

         ...

}

系统提供的是一个浅拷贝的函数,析构时就会出现问题:

第三句代用拷贝构造函数,方式是浅拷贝。运行的话程序会崩掉,因为good2,good3同时指向一块内存,调用析构函数时,good3会先析构内存块就会被释放,good2就变成了野指针,析构时delete就会崩溃。

C++中类和对象以及成员函数

先析构good3,内存块被释放,good2指向空,在此析构程序崩溃。

自己写的拷贝构造:

那么如果想要将下面这句构造成功,则需要:

CGoods good3=good2;

CGoods(const CGoods &rhs)

{

         _name=newchar[strlen(rhs._name)+1];

         strcpy(_name,rhs._name);

         _amount=rhs._amount;

         _price=rhs._price;

}

调用的拷贝构造函数必须传引用,为了防止实参和形参递归调用。

4、赋值运算符重载函数(用已存在的对象给另一个已存在的对象赋值)

系统提供的:

void operator=(const CGoods &rhs)

{

         _name=rhs._name;

         _amount=rhs._amount;

         _price=rhs._price;

}

good3=good2;//good3已经定义过,进行重新赋值

存在的问题:浅拷贝和内存泄漏。

C++中类和对象以及成员函数

good3的指向变了之后指向了内存块1,析构时内存块1被释放,产生野指针再析构时会崩溃,内存块2没有被释放会产生内存泄漏。

自己写的:

void operator=(const CGoods &rhs)

{

         if(this==&rhs)//防止自赋值

         {

             return;

         }

         delete[]_name;//释放旧资源,防止内存泄漏

         _name=newchar[strlen(rhs._name)+1];//开辟新资源进行初始化

         strcpy(_name,rhs._name);

         _amount=rhs._amount;

         _price=rhs._price;

}

这样就不会出现浅拷贝和内存泄漏的问题。