C++基础 - 类和对象

这篇博客详细介绍了C++中的类定义,包括成员访问、构造函数与析构函数、类的继承、抽象类以及友元类和友元函数的概念。此外,还讲解了运算符重载的规则和注意事项,帮助读者深入理解C++面向对象编程的基本概念和技术。
摘要由CSDN通过智能技术生成

端午节就复习一下C++基础吧。

目录

1.1 类的定义

1.2 类成员的访问

1.3 构造函数和析构函数

1.4 类的继承

1.4.1 实例化对象调用

1.4.2 指针类型调用:

1.4.3 抽象类

1.5 友元类和友元方法(函数)

1.6 运算符重载

1.1 类的定义

C++语言中,类和结构体类似,其中可以定义数据和方法,提供 class关键字定义类。

类的定义包括两部分,类头和类体。类头由 class关键字和类名组成,类体由一组大括号 “{}”和一个“;”组成类体中通常定义类的数据和方法,其中数据描述的是类的特征(也称之为属性)。方法实际上为类内定义的函数,描述的类的行为。

class CUser
{
    char m_Username[128];
    char m_Password[128];
  
    bool Login()
    {
        if(strcmp(m_Username,"MR") == 0  && strcmp(m_Password,"KJ") == 0 )
        {
            printf("登录成功!\n");
            return true;
        }
        else
        {
            printf("登录失败!\n");
            return false;            
        }
    }
};

        ·上述代码定义了一个CUser类,其中包含有两个数据成员和一个方法。对于方法的定义,也可以放在类外进行定义,方法名需要使用类名和限域限定符 " ::  "  ,依次来标记该方法属于哪一个类的方法。

class CUser
{
    char m_Username[128];
    char m_Password[128];

    bool Login()
    
}; 

bool CUser::Login()
{
    if(strcmp(m_Username,"MR") == 0  && strcmp(m_Password,"KJ") == 0 )
    {
        printf("登录成功!\n");
        return true;
    }
    else
    {
        printf("登录失败!\n");
        return false;            
    }
}

        注意:不可以对类内的数据成员进行初始化操作!

1.2 类成员的访问

        类成员主要是指类中的数据成员和方法,在定义类时,类成员是具有访问限制,C++提供有三种限定符来标识类成员的访问,分别为 pubilc、protected和private。

public成员称之为公共成员,该成员可以在程序中的任何地方进行访问。

protected成员称之为保护成员,该成员只能在该类和该类的派生类(子类)中进行访问,除此之外,程序的其他地方没有访问权限。

private成员称之为私有成员,该成员只能在该类中使用,派生类和其他地方没有访问权限。

在定义类时,没有设置访问限定符时,默认为private。

class CUser
{
private:
    char m_Username[128];
    char m_Password[128];

public:

    void SetUsername(const char* pUsername)
    {
        if(pUsername != Null)
        {
            strcpy(m_Username,pUsername)
        }  
    }

    char* GetUsername()const
    {
        return (char*)m_Username;
    }

    void SetPassword(const char* pPassword)
    {
        if(pPassword!= Null)
        {
            strcpy(m_pPassword,pPassword)
        }  
    }

    char* GetpPassword()const
    {
        return (char*)m_pPassword;
    }

    bool Login()
    {
        if(strcmp(m_Username,"MR") == 0  && strcmp(m_Password,"KJ") == 0 )
        {
            printf("登录成功!\n");
            return true;
        }
        else
        {
            printf("登录失败!\n");
            return false;            
        }
    }


};

 在定义类之后,需要访问类内成员时,通常类成员的防问是通过对象来实现的,对象被称之为类的实例化。当程序中定义一个类时,并没有为其分配存储空间,只有当定义类的对象时,菜分配存储空间。对象的定义和普通变量的定义是一样的。

        在定义类的对象之后,就可以访问类的成员了。

// 访问类成员通过对象的形式访问
CUser user;   

// 
user.SetUername("MR");
user.setPassword("KJ");
user.login();

        在定义类对象时,也可以将类对象声明声明为一个指针。在程序中使用new运算符来分配指针内存。

// 访问类成员通过声明一个指针的形式访问
CUser *puser = new CUser;   

// 或者如下也是合理
CUser *puser2 = new CUser();   

如果类的对象被定义为指针时,需要使用 ->运算符来访问类成员。需要注意的是,如果类对象定义为常量指针,则对象只允许调用const方法。

// 访问类成员通过声明一个指针的形式访问
CUser *puser = new CUser;   

user->SetUername("MR");
user->setPassword("KJ");
user->login();

delete pUser; // 记得要释放

 

1.3 构造函数和析构函数

        每个类都有构造函数和析构函数。其中 ,构造函数在定义对象时被调用,析构函数在对象释放时被调用。如果用户没有提供构造函数和析构函数时,系统讲提供默认的构造函数和析构函数。

构造函数:该函数是一个与类同名的方法,可以没有参数,也可以有一个或者多个参数。但是构造函数没有返回值。如果构造函数没有参数,该函数称之为类的默认构造函数。

class CUser
{
private:
    char m_Username[128];
    char m_Password[128];
public:
    CUser()   // 显式定义一个默认的构造函数
    {
        strcpy(m_Username,"MR")  // 在该函数内部给值进行赋值
        strcpy(m_Password,"kj")    
    }
    char* GetUsername()const
    {
        return (char*)m_Username;
    }

    char* GetpPassword()const
    {
        return (char*)m_pPassword;
    }

};
int main(int argc , char* argvx [])
{
    CUser user;
    printf("%s\n",user.GetpPassword());  // 会调用构造函数内部的值进行输出
    return 0;
}

说明: 一个类可以包含有多个构造函数,各个构造函数之间通过参数列表进行区分和调用。不再展开。

        前面说到,我们不可以在类体内直接对数据成员进行初始化赋值。那如果在类中包含有常量或引用类型的数据成员时,该如何初始化呢??

        类的构造函数通过使用 “ :: ” 运算符提供了初始化成员的方法。

class CBook
{
public:

    char m_BookName[128];
    const unsigned int m_price;
    int m_ChapterNum;

    CBook()                 // 在构造函数旁边使用 :: 运算符来对数据成员进行初始化
         :: m_price(15),m_ChapterNum(12) 
    {
        strcpy(m_BookName,"大学英语")
    }
    
};

析构函数: 该函数在对象超出作用范围或使用delete运算符释放对象时被调用,用于释放对象占有的空间。与构造函数一样,如果用户没有显示提供析构函数,系统会提供默认的析构函数。析构函数也时以类名作为函数名,与构造函数的区别就是需要在函数名前加一个 “ ~ ”。

        析构函数没有参数,因此不能重载。即一个类只会有一个析构函数。

1.4 类的继承

        继承是面向对象的主要特征之一,此外还有封装和多态。它使得一个类可以从现有的类中派生,而不用重新定义一个新类。

        例如一个员工类,其中包含有员工ID、姓名是、所属部门等信息。在定义一个操作员类,操作员也是属于公司的员工,因此该类中也会有员工类中的所有信息,还包含有密码信息、登录方法等新的属性。那么如果之前定义了员工类,咋在定义操作员类时可以从员工类派生出一个新的员工类,然后向该类中添加密码、登录方法等信息。

#define MAXLEN 128

class CEmployee
{
public:
    int m_ID;
    char m_Name[MAXLEN];
    char m_Depart[MAXLEN];

    CEmployee()
    {
        memset(m_name,0,MAXLEN);
        memset(m_Depart,0,MAXLEN);
        printf("员工类构造函数被调用\n");
    }
    
    void outputName()
    {
         printf("员工姓名: %s\n",m_Name);
    }
};


class COpreate : public CEmployee
{
public :
    
    char m_Password[MAXLEN];

     bool Login()
    {
        if(strcmp(m_Username,"MR") == 0  && strcmp(m_Password,"KJ") == 0 )
        {
            printf("登录成功!\n");
            return true;
        }
        else
        {
            printf("登录失败!\n");
            return false;            
        }
    }
   
};

        上述代码在定义 COpreate类时使用了 “  : ”运算符。表示该类派生与一个基类;public关键字表示派生的类型为公有性;其后的CEmployee表示COpreate的基类,也就是父类。这样 COpreate类将会继承 CEmployee 类中所有的非私有(public和protected)成员(private成员不能被继承)。

        当一个类从另一个类继承时,可以有3种派生类型,public、protected和private三种。

        派生类型为公有型时,基类中的public数据成员和方法在派生类中任然是public。基类中的protected在派生类中同样也是protected。即不会改变基类在派生类中的访问权限。

        派生类为保护型时,基类中的public和protected数据成员和方法在派生类中均为protected。

        派生类型为私有型时,基类中的public和protected数据成员和方法在派生类中均为private。

int main(int argc , char* argvx [])
{
    COpreate optr;                   // 定义一个访问COperator类对象
    strcpy(optr.m_Name,"MR");       // 访问基类的 m_Name 成员
    strcpy(optr.m_Password,"KJ");   // 访问 COpreate 中  m_Password 成员
    optr.login();                   // 访问 COpreate 中  login 方法

    optr.OutputName();              // 访问基类中的 OutputName()的方法。
    return 0;
}

1.4.1 实例化对象调用

        当用户在父类中派生子类时,可能存在一种情况,即在父类中定义了一个与父类的方法同名的方法函数。这种情况,该子类的方法会隐藏父类的方法。

class COpreate : public CEmployee
{
public :
    
    char m_Password[MAXLEN];

    void outputName()  // 父类 CEmployee 中也有该方法,此时会隐藏父类的方法
    {
         printf("操作员姓名: %s\n",m_Name);
    }


     bool Login()
    {
        if(strcmp(m_Username,"MR") == 0  && strcmp(m_Password,"KJ") == 0 )
        {
            printf("登录成功!\n");
            return true;
        }
        else
        {
            printf("登录失败!\n");
            return false;            
        }
    }
   
};


int main(int argc , char* argvx [])
{
    COpreate optr;                   // 定义一个访问COperator类对象
    strcpy(optr.m_Name,"MR");       // 访问基类的 m_Name 成员
    
    optr.OutputName();              // 会访问COpreate 中的 OutputName()的方法。
    return 0;
}

上述代码会输出 :操作员姓名 :MR  。会调用  COpreate  类中的outputName方法,而不是基类中的outputName方法。

        如果用户向调用基类中的outoutName()方法,需要加一个显示调用。

int main(int argc , char* argvx [])
{
    COpreate optr;                   // 定义一个访问COperator类对象
    strcpy(optr.m_Name,"MR");       // 访问基类的 m_Name 成员
    
    optr.OutputName();              // 会访问 COpreate 中的 OutputName()的方法。
    optr.CEmployee::OutputName();   // 会访问 CEmployee中的 OutputName()的方法。
    return 0;
}

        如果子类隐藏了父类的方法,那么父类中所有的同名的方法(重载方法),均会被隐藏!!

1.4.2 指针类型调用:

         在派生一个子类后,也可以运用一个父类的类型指针,通过对子类的构造函数为其创建对象。


CEmployee *pWorker = new COpreate (); // 定义CEmployee 基类指针,调用子类的构造函数

        如果此时,使用 pWorker对象去调用outputName方法,如果执行  pWorker ->outputName();那此时是执行 基类中outputName()方法还是子类中的outputName()方法呢?

        答案是会执行基类  CEmployee  中的  outputName()方法。

        编译器对 outputName()方法进行的是静态绑定,即根据对象定义时的类型来确定调用的是那个类的方法,由于  pWorker 指针是 CEmployee 类型,所有会调用基类 CEmployee的方法。

        那如果我需要调用子类 COpreate 中的 outputName()方法需要怎么实现呢?通过定义虚方法可以实现这一点。

        在定义方法(成员函数)时,在方法的前面使用virtual关键字,该方法即为虚方法,使用虚方法可以实现类的动态绑定,即根据对象运行时的类型来确定调用那个类的方法,而不是根据对象定义时的类型来确定调用那个方法。

#define MAXLEN 128

class CEmployee
{
public:
	int m_ID;
	char m_Name[MAXLEN];
	char m_Depart[MAXLEN];

	CEmployee()
	{
		memset(m_Name, 0, MAXLEN);
		memset(m_Depart, 0, MAXLEN);
		printf("员工类构造函数被调用\n");
	}

	virtual void outputName()  // 虚函数
	{
		printf("员工姓名: %s\n", m_Name);
	}
};


class COpreate : public CEmployee
{
public:

	char m_Password[MAXLEN];

	void outputName()  // 父类 CEmployee 中也有该方法,此时会隐藏父类的方法
	{
		printf("操作员姓名: %s\n", m_Name);
	}

	bool Login()
	{
		if (strcmp(m_Name, "MR") == 0 && strcmp(m_Password, "KJ") == 0)
		{
			printf("登录成功!\n");
			return true;
		}
		else
		{
			printf("登录失败!\n");
			return false;
		}
	}

};

int main()
{
	CEmployee *pWorker = new COpreate();

	strcpy_s(pWorker->m_Name, "MR");
	pWorker->outputName();
	delete pWorker;

    return 0;
}

        上述代码既可实现,父类指针实现派生类的方法。

1.4.3 抽象类

        在C++语言中,除了能够定义虚方法外,也有定义纯虚方法,也就是通常所说的抽象方法。u一个包含纯虚方法的类称之为抽象类,抽象类不能被实例化。通常用于实现接口的定义。

注意:抽象类不能实例化  ( CEmployee Worker;违规操作)

#define MAXLEN 128
class CEmployee   // 包含抽象方法的类称之为抽象类
{
public:
	int m_ID;
	char m_Name[MAXLEN];
	char m_Depart[MAXLEN];

	virtual void outputName() = 0; // 定义抽象方法
};


class COpreate : public CEmployee
{
public:
	char m_Password[MAXLEN];

	void outputName()  // 
	{
		printf("操作员姓名: %s\n", m_Name);
	}
	COpreate()
	{
		strcpy_s(m_Name, "MR");
	}
};

class CSystemManger : public CEmployee
{
public:
	char m_Password[MAXLEN];

	void outputName()  
	{
		printf("系统管理员姓名: %s\n", m_Name);
	}
	CSystemManger()
	{
		strcpy_s(m_Name, "SK");
	}
};

int main()
{
	CEmployee *pWorker;
	pWorker = new COpreate();
	pWorker->outputName();  // 调用 COpreate 类对象的方法
	delete pWorker;
	pWorker = NULL;

	pWorker = new CSystemManger();
	pWorker->outputName();  // 调用 CSystemManger 类对象的方法
	delete pWorker;
	pWorker = NULL;

    return 0;
}

        注: 抽象类通常作为其他类的父类,从抽象类派生的子类如果也是抽象类,则子类必须实现父类中所有的纯虚方法。

实例化类对象的创建和释放过程:

        当从父类派生一个子类后,定义一个子类的对象时,它将依次调用父类的构造函数、当前类的构造函数来创建对象。在释放子类对象时,先调用的是当前类的析构函数,然后是父类的析构函数。

指针类对象的创建和释放的过程:

        定义一个基类类型的指针,调用子类的构造函数为其创建对象时。对象释放后,如果析构函数为虚函数,则先调用子类的析构函数,再调用基类的析构函数。如果析构函数不是虚函数时,则只会调用基类的析构函数(会发生内存泄露,因为子类中会为某一些数据成员在堆中分配了了空间,程序结束却没有释放)。

        所以析构函数最好是保证是虚函数!!!

1.5 友元类和友元方法(函数)

        类的私有方法,只有在该类中才允许访问,其他类是不能访问的,打死你hi如果两个类的耦合度比较紧密,在一个类中访问另一个类的私有成员就会带来极大的方便。C++提供了友元类和友元函数,来实现类与类之间的函数方法的访问。

        当用户希望另一个类能够访问当前类的私有成员时,可以在当前类中将另一个类作为自己的友元类。


class CItem
{
private:
	char m_Name[128];
	void outputname()
	{
		printf("%s/n",m_Name);
	}
public:
	friend class CList;    // 将CList类定义一个CItem的友元类
	void SetItemName(const char * pchData)
	{
		if (pchData != NULL)
		{
			strcpy_s(m_Name, pchData);
		}
	}
	CItem()
	{
		memset(m_Name,0, 128);
	}

};

class CList
{
public:
	void outputItem();
private:
	CItem m_Item;
};

void CList::outputItem()
{
	m_Item.SetItemName("BIEJING");  // 调用 CItem类的公有方法
	m_Item.outputname();			// 调用 CItem类的私有方法
}

         除了上述代码所展示的友元类之外,还可以在某一个类中定义其他类的友元方法。另外,友元方法(函数)也可以是一个全局函数。

1.6 运算符重载

         运算符重载需要遵守如下规则和注意事项;

注意事项:

1、并不是所有的C++运算符都可以重载。比如“ ::”,“?”,“:”,“.”四个特殊运算符不能重载,其他均可。

限制:

1、不能创建新的运算符;

2、不能改变原有运算符操作数的个数;

3、不能改变又原有运算符的优先级;

4、不能改变原有运算符的结合性;

5、不能改变原有运算符的语法结构;

基本准则:

1、一元操作数可以是不带参数的成员函数。或者是带一个参数的非成员函数

2、二元函数可以是带一个参数的成员函数,或者是带两个参数的非成员函数、

3、 ” = “、” [] “、” ->“、“ ()”运算符只能定义为成员函数。

4、” ->“运算符和返回值必须是指针类型或者能够使用 ” ->“ 的运算符对象。

5、重载 ++ 和 -- 运算符时,带一个int类型参数,表示后置运算,不带参数表示前置运算。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值