C++面向对象编程入门:类(class)

我们在c语言中创建一个结构体我们使用如下方法:

struct  test 

    private
        int  number; 
    public
        float  socre; 
};

  类的创建方式和结构体几乎一样,看如下的代码:

class  test 

    private
        int  number; 
    public
        float  socre; 
    public
        int  rp() 
        { 
            return  number; 
        } 
        void  setnum(int  a) 
        { 
            number=a; 
        } 
};

  但是大家注意到没有,标准c中是不允许 在结构体中声明函数 ,但c++中的类可以,这一点就和c有了本质的区别,很好的体现了c++面向对象的特点!

  过去的c语言是一种非面向对象的语言

  他的特性是:

  程序=算法+数据结构

  但c++的特性是

  对象=算法+数据结构

  程序=对象+对象+对象+对象+........

  所以根据这一特性,我们在定义一个自己定义的结构体变量的时候。这个变量就应该是叫做对象或者叫实例。

  例如

test a;

  那么a就是test结构的一个对象(实例)

  test结构体内的成员可以叫做是分量,例如:

a.socre=10.1f;

  那么number就是test结构的对象a的分量(或者叫数据成员,或者叫属性)score;

   在c语言中结构体中的各成员他们的默认存储控制是public 而 c++中类的默认存储控制是private,所以在类中的成员如果需要外部掉用一定要加上关键字public声明成公有类型,这一特性同样使用于类中的成 员函数,函数的操作方式和普通函数差别并不大。

 

例如上面的例子中的rp()成员函数,我们如果有如下定义:

test a;

  的话,调用rp()就应该写成:

a.rp();

  成员函数的调用和普通成员变量的调用方式一致都采用.的操作符。

  这一小节为了巩固联系我给出一个完整的例子。

  如下(重要和特殊的地方都有详细的注解):

#include <iostream
using  namespace  std; 
class  test 

    private ://私有成员类外不能够直接访问 
        int  number; 
    public ://共有成员类外能够直接访问 
        float  socre; 
    public
        int  rp() 
        { 
            return  number; 
        } 
        void  setnum(int  a) 
        { 
            number=a; 
        } 
}; 
 
void  main () 

    test a; 
    //a.number=10;//错误的,私有成员不能外部访问 
    a.socre=99.9f; 
    cout <<a.socre<<endl;//公有成员可以外部访问 
    a.setnum(100);//通过公有成员函数setnum()间接对私有成员number进行赋值操作 
    cout <<a.rp();//间接返回私有成员number的值 
    cin .get(); 
}

  好了,介绍了在类内部定义成员函数(方法)的方法,下面我们要介绍一下域区分符(::)的作用了。

  下面我们来看一个例子,利用这个例子中我们要说明两个重要问题:

#include <iostream
using  namespace  std; 
int  pp=0; 
class  test 

    private
        int  number; 
    public
        float  socre; 
        int  pp; 
    public
        void  rp(); 
}; 
void  test::rp()//在外部利用域区分符定义test类的成员函数 

    ::pp=11;//变量名前加域区分符给全局变量pp赋值 
    pp=100;//设置结构体变量 

 
void  main () 

    test a; 
         test b; 
    a.rp(); 
    cout <<pp<<endl; 
    cout <<a.pp<<endl; 
     
    cin .get(); 
}

问题1:


  利用域区分符我们可以在类定义的外部设置成员函数,但要注意的是,在类的内部必须预先声明:

void test::rp()

  在函数类型的后面加上类的名称再加上域区分符 (::)再加函数名称,利用这样的方法我们就在类的外部建立了一个名为rp的test类大成员函数(方法),可能很多人要问,这么做有意义吗?在类的内部写函数代码不是更好?

  答案是这样的:在类的定义中,一般成员函数的规模一般都比较小,而且一些特殊的语句是不能够使用的,而且一般会被自动的设置成为inline(内联)函数 ,即使你没有明确的声明为inline,那么为什么有会被自动设置成为inline呢?因为大多数情况下,类的定义一般是放在头文件中的,在编译的时候这些函数的定义也随之进入头文件,这样就会导致被多次编译,如果是inline的情况,函数定义在调用处扩展 , 就避免了重复编译的问题,而且把大量的成员函数都放在类中使用起来也十分不方便,为了避免这种情况的发生,所以c++是允许在外部定义类的成员函数(方 法)的,将类定义和其它成员函数定义分开,是面向对象编程的通常做法,我们把类的定义在这里也就是头文件了看作是类的外部接口,类的成员函数的定义看成是 类的内部实现。写程序的时候只需要外部接口也就是头文件即可,这一特点和我们使用标准库函数的道理是一致 的,因为在类的定义中,已经包含了成员函数(方法)的声明。

  问题二

  域区分符和外部全局变量和类成员变量之间的关系。

  在上面的代码中我们看到了,外部全局和类内部都有一个叫做pp的整形变量,那么我们要区分操作他们用什么方法呢?

  使用域区分符就可以做到这一点,在上面的代码中::pp=11;操作的就是外部的同名称全局变量,pp=100;操作的就是类内部的成员变量,这一点十分重要!

  问题三

  一个类的所有对象调用的都是同一段代码,那么操作成员变量的时候计算机有是如何知道哪个成员是属于哪个对象的呢?

  这里牵扯到一个隐藏的this指针 的问题,上面的代码在调用a.rp()的的时候,系统自动传递一了个a对象的指针给函数,在内部的时候pp=100;的时候其实就是this->pp=100;

  所以你把上面的成员函数写成如下形势也是正确的:

void  test::rp() 

    ::pp=11; 
    this ->pp=100;//this指针就是指向a对象的指针 
}

  类的成员函数和普通函数一样是可以进行重载操作的,关于重载函数前面已经说过,这里不再说明。

  给出例子仔细看:

#include <iostream >   
using  namespace  std;   
class  test   
{   
    private :   
        int  number;   
    public :   
        float  socre;   
        int  pp;   
    public :   
        void  rp(int ); 
        void  rp(float ); 
};   
void  test::rp(int  a)//在外部利用域区分符定义test类的成员函数   
{   
    cout <<"调用成员函数!a:"<<a<<endl; 
}   
void  test::rp(float  a)//在外部利用域区分符定义test类的成员函数   
{   
    cout <<"调用成员函数!a:"<<a<<endl; 

void  main ()   
{   
    test a; 
    a.rp(100); 
    a.rp(3.14f); 
    cin .get();   
}

 

 

 下面我们来看一下利用指针和利用引用间接调用类的成员函数,对于对于指针和引用调用成员函数和调用普通函数差别不大,在这里也就不再重复说明了,注意看代码,多试多练习既可。

  代码如下:

#include <iostream >   
using  namespace  std;   
class  test   
{   
    private :   
        int  number;   
    public :   
        float  socre;   
        int  pp;   
    public :   
        int  rp(int ); 
};   
int  test::rp(int  a)//在外部利用域区分符定义test类的成员函数   
{   
    number=100; 
    return  a +  number; 

 
void  run(test *p)//利用指针调用 

    cout <<p->rp(100)<<endl; 

void  run(test &p)//利用引用 

    cout <<p.rp(200)<<endl; 

 
void  main ()   
{   
    test a; 
    run(&a); 
    run(a); 
    cin .get();   
}

  前面我们说过,类的成员如果不显式的生命为public那么它默认的就是private就是私有的,私有声明可以保护成员不能够被外部访问,但在c++还有一个修饰符,它具有和private相似的性能,它就是protected修饰符。

  在这里我们简单说明一下,他们三着之间的差别:

  在类的private:节中声明的成员(无论数据成员或是成员函数)仅仅能被类的成员函数和友元访问。
  在类的protected: 节中声明的成员(无论数据成员或是成员函数)仅仅能被类的成员函数,友元以及子类的成员函数和友元访问。
  在类的public:节中声明的成员(无论数据成员或是成员函数)能被任何人访问。


  由于private和protected的差别主要是体现在类的继承中,现在的教程还没有设计到友元和子类所以这里不做深入讨论,但上面的三点务必记得,在以后的教程中我们会回过头来说明的。

  总的来说,类成员的保护无非是为了以下四点 !

  1.相对与普通函数和其它类的成员函数来说,保护类的数据不能够被肆意的篡改侵犯!

  2.使类对它本身的内部数据维护负责,只有类自己才能够访问自己的保护数据!

  3.限制类的外部接口,把一个类分成公有的和受保护的两部分,对于使用者来说它只要会用就可以,无须了解内部完整结构,起到黑盒的效果。

  4.减少类与其它代码的关联程,类的功能是独立的,不需要依靠应用程序的运行环境,这个程序可以用它,另外一个也可以用它,使得你可以轻易的用一个类替换另一个类。


  下面为了演示类成员的保ぬ匦裕颐抢醋鲆桓銮蚶嘤蜗罚?

  我们设计一个类,来计算球员的平均成绩,要求在外部不能够随意篡改球员的平均成绩。

  我们把该类命名为ballscore并且把它放到ballscore.h的有文件中!

class  ballscore 
{   
    protected
        const  static  int  gbs =  5;  //好球单位得分 
        const  static  int  bbs =  -3;  //坏球单位扣分 
        float  gradescore;  //平均成绩 
    public
        float  GetGS(float  goodball,float  badball)  //goodball为好球数量,badball为坏求数量 
        { 
            gradescore =  (goodball*gbs +  badball*bbs) / (goodball +  badball); 
            return  gradescore;  //返回平均成绩 
        } 
};

 

 主函数调用:

#include <iostream >   
#include "ballscore.h" 
using  namespace  std;   
 
void  main ()   
{   
    ballscore jeff; 
    cout <<jeff.GetGS(10,3); 
    jeff.gradescore=5.5//想篡改jeff的平均成绩是错误的! 
    cin .get(); 
}

  在上面的代码中头文件和类的使用很好了体现了类的黑盒特性,谁也不能够在外部修改球员的平均成绩!

  类体中的有一个地方要注意

const static int gbs = 5;//好球单位得分
const static int bbs = -3;//坏球单位扣分

  之所以要修饰成const static 因为c++中类成员只有静态整形的常量才能够被初始化,到这里整个程序也就说完了,当然真正大比赛不可能是这样,只是为了说明问题就题命题而已,呵呵!

  下面我们说一下关于类的作用域

  在说内容之前我们先给出这部分内容的一个完整代码,看讲解的是参照此一下代码。

#include <iostream >   
using  namespace  std;   
class  ballscore 
{   
    protected
        const  static  int  gbs =  5;//好球单位得分 
        const  static  int  bbs =  -3;//坏球单位扣分 
        float  gradescore;//平均成绩 
    public
        float  GetGS(float  goodball,float  badball)  //goodball为好球数量,badball为坏求数量 
        { 
            int  gradescore=0; 
   //新定义一个和成员变量float gradescore相同名字的类成员函数局部变量 
            ballscore::gradescore =  (goodball*gbs +  badball*bbs) /
            (goodball +  badball);  //由于局部变量与类成员变量同名使用的时候必须在其前加上类名和域区分符 
            return  ballscore::gradescore;//返回平均成绩 
        } 
}; 
int  ballscore=0;//定义一个与类名称相同的普通全局变量 
int  test; 
void  main () 

    class  test//局部类的创建 
    { 
        float  a; 
        float  b; 
    }; 
    test test; 
    ::test=1;  //由于局部类名隐藏了外部变量使用需加域区分符 
    class  ballscore jeff;  //由于全局变量int ballsocre和类(ballsocre)名称相同,隐藏了类名称,这时候定义类对象需加class前缀以区分 
    cout <<jeff.GetGS(10,3); 
    cin .get(); 
}

  类的作用域是只指定义和相应的成员函数定义的范围,在该范围内,一个类的成员函数对同一类的数据成员具有无限制的访问权。

 

在类的使用中,我们经常会碰到以下三种情况:

  1.类的成员函数的局部变量隐藏了类的同名成员变量 ,看如对上面程序的分析。

    protected
        const  static  int  gbs =  5; 
        const  static  int  bbs =  -3; 
        float  gradescore; 
    public
        float  GetGS(float  goodball,float  badball) 
        { 
            int  gradescore=0; 
            ballscore::gradescore =  (goodball*gbs +  badball*bbs) /
           (goodball +  badball); 
            return  ballscore::gradescore; 
        }

  代码中的int gradescore就把float gradescore给隐藏了,所以要使用成员变量float gradescore的时候必须在其之前加上类名称和域区分符(::)。

  2.在类定义外部非类型名隐藏了类型名称的情况 ,看上面代码的分析!

class ballscore
{
    protected
        const  static  int  gbs =  5; 
        const  static  int  bbs =  -3; 
        float  gradescore; 
    public
        float  GetGS(float  goodball,float  badball) 
        { 
            int  gradescore=0; 
            ballscore::gradescore =  (goodball*gbs +  badball*bbs) / 
            (goodball +  badball); 
            return  ballscore::gradescore; 
        }
};
int ballscore=0;

  代码中的全部变量int ballscore隐藏了类名称class ballscore

  所以在main中如如果要定义ballscore类的对象就要在类名称前加上class关键字

class ballscore jeff;

  3.类型名称隐藏了非类型名称 ,看对上面代码的分析

int  test; 
void  main () 

    class  test 
    { 
        float  a; 
        float  b; 
    }; 
    test test; 
    ::test=1; 
    class  ballscore jeff; 
    cout <<jeff.GetGS(10,3); 
    cin .get(); 
}

  在普通函数内部定义的类叫做局部类,代码中的test类就是一个局部类!

  代码中的test类隐藏了全局变量test如果要操作全局变量test那么就要在test前加上域区分符号(::),进行使用!

  ::test=1就是对全局变量test进行了赋值操作。

  我们最后说一下名字空间!

  名字空间就是指某一个名字在其中必须是唯一的作用域.

  如果这个定义想不明白,可以简单的说成,在一个区域内,某一个名字在里面使用必须是唯一的,不能出现重复定义的情况出现,这个区域就是名字空间!

 

 

 c++规定:

  1.一个名字不能同时设置为两种不同的类型

class test
{
//...
};
typedef int test;

这个就是错误的!

  2.非类型名(变量名,常量名,函数名,对象名,枚举成员)不能重名.

test a;
void a();

  就是错误的,因为a是一个test类的对象,它和函数a名称重名了!

  3.类型与非类型不在同一个名字空间上,可以重名,即使在同一作用域内,但两者同时出现时定义类对象的时候要加上前缀class以区分类型和非类型名!

class test
{
//.....
}

int test

class test a;//利用class前坠区分,定义了一个test类的对象a

  好了,到这里关于类的知识点我们已经学习完,希望大家多多练习

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《Java程序设计》课程实验指导书程序代码(答案)(实验二),个人原创,仅供参考与交流。 希望多多交流,共同进步! 实验二 Java语言基础 一、实验目的: 熟悉Java基本语法,基本数据型,各种运算符及表达式的使用,掌握运算符优先级,熟悉使用Java的选择语句,循环语句。 二、实验内容: 1.编写Java Application程序,输出1900年到2000年之间的所有润年。(闰年的判断条件:能被4整除且不能被100整除,或能被400整除); 2.编写Java Appet 程序打印 “水仙花” 数 (它的个、十、百位数字的立方的和等于该数本身,如:153=1^3+5^3+3^3) 3. 编写Java Application程序,分别用do-while和for循环计算1+1/2!+1/3!+1/4!...的前20项和 三、实验要求: 1. 正确使用Java语言的选择语句,循环语句; 2. 调试程序、编译,运行后得到正确的结果 3.写出实验报告。要求记录编译和执行Java程序当中的系统错误信息提成示,并给出解决办法。 四、实验步骤: 1.编写主; 2.在static public void main(String[ ] args)方法中加入实现要求功能的代码,主要步骤如下: (第一题)从1900到2000循环,按照闰年的判断条件是则输出1900年到2000年之间的所有润年。 (第二题)编写Java Applet, 在public void paint(Graphics g)方法中加入实现要求功能的代码, 主要步骤是:从100到1000循环,判断每个数是否符合水仙花数的特点,是则输出之。 3.编译运行程序,观察输出结果是否正确。 五、自做实验 1. 输出100以内的所有素数。 提示: 逐个判断小于a的每个正整数x,第二重循环针对x,判断其是否是质数。 2.将所输入之正整数,以二、八、十六进制表示出来。 提示: 可写三个方法分别计算输出二、八、十六进制表示。
MFC编程(Microsoft Foundation Class)是指使用Microsoft Visual C++开发Windows平台应用程序的一种编程技术。下面是关于MFC编程入门到精通的回答。 首先,入门级的MFC编程需要掌握C++语言的基础知识,包括变量、函数、面向对象的概念等。同时,需要了解Windows操作系统的基本原理和用户界面的设计与开发。 接下来,学习MFC框架的使用是非常重要的。MFC提供了一套丰富的库,用于简化Windows应用程序的开发。需要熟悉MFC中的基本,如CWinApp、CFrameWnd、CDialog等,以及它们的成员函数和消息映射等概念,掌握MFC框架的基本使用方法。 在掌握基本的MFC编程技能后,深入学习MFC的各种功能和扩展,可以提高编程的灵活性和效率。这包括了处理消息、控件的操作、对话框的设计、文件的读写、绘图、多线程编程等高级主题。 此外,了解一些高级的MFC技术和技巧,如自定义控件、自绘界面、多文档界面、打印和预览、国际化支持等,可以让你的应用程序更加专业和用户友好。 要精通MFC编程,则需要不断实践和积累经验。可以通过阅读MFC编程相关的书籍、教程和文档,参加相关的培训和讨论,以及积极参与开发社区的交流,与其他开发者分享经验和技术。 总结起来,MFC编程入门到精通需要掌握C++语言基础、MFC框架的使用、高级技术和扩展,以及不断实践和积累经验。通过不断学习和实践,可以成为一名熟练的MFC开发者,并能够高效地开发出功能丰富、稳定可靠的Windows应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值