面向对象初探

本文详细介绍了C++中的类,包括成员变量、成员函数、访问修饰符(public、private、protected)的使用。特别讨论了构造函数和析构函数的作用,以及拷贝构造函数的调用场景和作用。同时,通过实例展示了拷贝构造函数在对象初始化和作为参数传递时的执行过程,强调了其在内存管理和对象复制中的重要性。
摘要由CSDN通过智能技术生成

c++类

​ 话不多说,看一段代码:

class Box
{
   public:
      double length;   // 长度
      double breadth;  // 宽度
      double height;   // 高度
};

该段代码中,使用class创建一个Box类,可以初始化一个对象

Box Box1

非常简单的初始化了一个Box1对象

这个时候,Box1就可以用**(.)成员访问运算符访问public里面的成员**,对于privateprotect的成员,不能用(.)进行访问。注意一点,成员默认为private的。

具体如何访问,后面再记!

类成员函数

​ 首先,理解什么是类成员函数,既然有“成员”两个字,那么肯定是属于类的一个成员,就跟定义类中其他成员一样。

​ 类成员函数可以操作类的对象,也可以访问对象的成员。

​ 举个例子:

class Box
{
   public:
      double length;         // 长度
      double breadth;        // 宽度
      double height;         // 高度
      double getVolume(void);// 返回体积
};

在类中声明了一个getVolume函数,既然是在类里面,那肯定是成员函数。下面来创建一个对象:

Box Box1

Box1就可以使用这个成员函数,怎么使用呢?用成员访问运算符

(跟上面说的 public 成员一样的)。

Box1.getVolume()

就可以使用这个成员函数了。

这个成员函数只有声明,没有定义函数体。有两种方法:

第一种:在类里面定义

class Box
{
   public:
      double length;      // 长度
      double breadth;     // 宽度
      double height;      // 高度
 
      double getVolume(void)
      {
         return length * breadth * height;
      }
};

直接写函数体内容就完事,注意:上面说过,成员函数是可以访问对象的成员的哦!

第二种:在类外面定义

double Box::getVolume(void)
{
    return length * breadth * height;
}

看到这个形式了吧,一定要写类名!然后用范围解析运算符 ::

进行定义!

然后发现长宽高都没有定义啊,return了个寂寞。

其实可以写多个类成员函数,进行初始化赋值。

后面有个简便初始化赋值的方法——构造函数。(后面再说)

类访问修饰符

​ 类访问修饰符分为三个:public private protected

​ 先说第一个 public

这个比较熟悉,可以被外部访问,那么什么算是外部呢?其实就是可以被对象直接使用,比如:

class Box
{
   public:
      double length;   // 长度
      double breadth;  // 宽度
      double height;   // 高度
};

在main函数中声明一个对象 Box Box1 。

那么要对成员进行初始化,两个方法:要么写一个类成员函数,对其进行赋值,还有一种,重点:直接进行赋值。

就是 **Box1.length = 2.0 …**直接进行访问,因为length是开放的。

​ 然后就是 private :

只有类和友元函数能访问私有成员。

就是说不能被对象在外部直接访问,但是可以用公有的成员函数进行访问赋值。(为什么要强调公有呢?因为公有才可以调用,如果设置私有成员函数,那还怎么调用它?)

​ 然后就是protected :

这个和私有成员差不多,有一点:其派生类可以访问!

举个栗子(上代码):

class Box
{
   protected:
      double width;
};
 
class SmallBox:Box // SmallBox 是派生类
{
   public:
      void setSmallWidth( double wid );
      double getSmallWidth( void );
};

定义一个Box 类,派生了一个SmallBox类

Box有个protected成员,SmallBox有两个成员函数

// 子类的成员函数
double SmallBox::getSmallWidth(``void``)
{
   return width ;
}
void SmallBox::setSmallWidth( ``double` `wid )
{
  width = wid;
}

看看main函数:

int main( )
{
   SmallBox box;
 
   // 使用成员函数设置宽度
   box.setSmallWidth(5.0);
   cout << "Width of box : "<< box.getSmallWidth() << endl;
 
   return 0;
}

声明一个SmallBox类的对象box,box能访问父类里的width,进行赋值,这就是一个很好的例子。想一想,能不能直接访问呢?

就是 box.width = 5.0 ,动手试一试。

运行结果是不行的,为什么不行,不是可以访问吗?

忽略了一点,protected是跟private相似,回顾一下private的访问方法,只能被类和成员函数访问,既然SmallBox继承了Box,那么Box就继承了父类的属性,SmallBox的成员函数就能访问private成员,即访问父类的类似于private的protected成员!

构造函数和析构函数

​ 构造函数属于类的一种特殊的成员函数,与类同名,在创建新对象的时候会调用,没有返回值,可以有参数。带参数的构造函数一般用于类成员的初始化。

​ 对于析构函数,一般在删除对象的时候会执行,析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

​ 如果程序里没有构造函数和析构函数,编译器在编译的时候会自动生成构造函数和析构函数,只是函数内没有任何操作。

拷贝构造函数

​ 看下方一段代码

#include<iostream>
using namespace std;

class Line{

public:
	int Getlentgh(void);
	Line(int len);
	Line(const Line& obj);
	~Line();
private:
	int* ptr;
};

Line::Line(int len) {
	cout << "调用构造函数为指针分配内存!" << endl;
	ptr = new int;
	*ptr = len;
}

Line::Line(const Line &obj) {
	cout << "调用拷贝构造函数为指针分配内存!" << endl;;
	ptr = new int;
	*ptr = *obj.ptr + 1;
}

Line::~Line() {
	cout << "释放内存" << *ptr <<endl;
	delete ptr;
}

int Line::Getlentgh(void) {
	return *ptr;
}

void display(Line obj) {
	cout << obj.Getlentgh() << endl;
}

int main()
{
	Line line1(10);
	Line line2 = line1; // 这里会调用拷贝构造函数
	display(line1);
	display(line2);
	return 0;
}

下面是输出:

调用构造函数为指针分配内存!
调用拷贝构造函数为指针分配内存!
调用拷贝构造函数为指针分配内存!
11
释放内存11
调用拷贝构造函数为指针分配内存!
12
释放内存12
释放内存11
释放内存10

说说我的分析:

​ 在main函数内,创建了一个line1对象,那么,肯定会先调用构造函数,因为构造函数会在创建新对象的时候调用,所以输出的第一行是line1的构造函数执行了。

​ 现在看后面的拷贝构造函数调用,是怎么回事呢?先来看看什么是拷贝构造。

拷贝构造函数是一种特殊的构造函数,它在创建对象时,是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于(被调用于):

① 通过使用另一个同类型的对象来初始化新创建的对象。

② 复制对象把它作为参数传递给函数。

③ 复制对象,并从函数返回这个对象。

拷贝构造函数的主体为:

classname (const classname &obj) {
   // 构造函数的主体
}

​ 对于拷贝构造函数的用途,很显然,该段示例中用了两种——①和②,且听我慢慢道来。

​ 既然说可以通过一个同类型对象初始化新创建的对象,那么在代码中,利用line1line2 进行了初始化,那么肯定会调用一次拷贝构造函数,所以输出的第二行就是这么来的。

​ 再看输出的第三行的拷贝构造函数,怎么来的呢?

​ 因为将一个对象当成形参传递给函数时,也会调用拷贝构造函数,所以在main函数中,**display(line1)**就会调用拷贝构造函数,就是这么来的,传递给函数后,由于调用了拷贝构造函数,它的值加一,所以会打印出11。

​ 可能又会问,不是调用了两次吗,为什么不是12呢?

因为第一次调用,是给line2对象进行初始化,所以line2的初始是10,然后执行的是line2的拷贝构造函数,line2的变成11,这时候line1的还是10,当第二次执行拷贝构造函数的时候,就是把line1作为参数传进display,所以第二次执行的是line1的拷贝构造函数。

​ 然后display()执行结束,把形参(局部)line1给释放掉了,所以会调用局部的line1的析构函数。

​ 然后又是一个执行拷贝构造函数,不用多说,肯定是line2作为参数传进display(),给调用了一次,还记得初始化调用了一次吗,那时候为11,这时候又调用,那就为12了,然后打印12出来,释放参数。

​ 重点来了,为什么最后两个释放呢?为什么一个11,一个10?

​ 先知道,肯定是return 0,结束了,然后执行了line1和line2的析构函数,这个没毛病吧?那个值是怎么回事呢?

​ 先给个答案,11的那个是line2 的,10的那个是line1 的。为什么呢?

因为在初始化line2的时候,调用了一次拷贝构造函数,那时候line2的变成了11,注意,没有被释放!只释放了变成12的那次!但是line1的是通过作为参数的方式传进函数然后执行拷贝构造函数的,会被释放掉,所以还是初始值10。

SO,大体就是这样,所以要牢记调用拷贝构造函数的条件!注意第三点:对象作为函数返回值也会调用一次!

​ 拷贝也有深拷贝和浅拷贝,后面再说!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kcoren

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值