c++对象和类

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zouxu634866/article/details/88245220

前言:继续复习c++ primer plus 以用来查漏补缺,这一次复习对象和类这一节


一、抽象和类

1.我们在设计书写类时,正规的写法是将类的定义放在头文件中,类的定义放在cpp中,另外类的实现也应该在另外一个cpp中。

头文件的书写格式:

#ifndef STUDENT_H
#define STUDENT_H
#include <iostream>

class student
{
private:
    int age;
    int level;

public:
    student();

    student(int c ,int d);

    void getage();
};

#endif // STUDENT_H

2.访问修饰符:public 和 private
private里面放的通常是数据成员,当然也可以放函数成员(少),public里面放的通常是方法(成员函数)。

注意:private里面的数据成员,只能通过共有成员函数(或友元函数)访问,也就是说private里的成员也不能通过对象实现,加入age是一个私有成员,小红是一个对象,则不能使用小红.age来访问,我们通常可以写一些接口函数来访问这些私有成员,比如函数:int getage()和void setage(),这样对象就可以通过调用这些接口函数来访问这些私有数据成员

3.构造函数。
我们在设计类时,默认构造和自定义构造(含参数构造函数)都要有。另外,要注意构造函数是属于public的,所以为了方便我们通常先写private,在写public,构造函数和析构函数要写在public里面。
我们在书写构造函数时,也要遵循声明在头文件,定义在cpp里面,cpp里面的书写格式:student :: student() {}

4.我们可以将一个对象赋值给另一个同类型的对象,比如: zouxu = yuzi;

此时,我们就把右边对象的每个数据成员的值赋值给了左边。

5.const 成员函数。
原则上,如果一个成员函数没有修改数据成员的操作,我们就需要将这个成员函数定义为const,格式就是在函数的末尾加上const,注意,声明和定义的末尾都要加上。
比如:

 void show()const;
 void stock::show() const
 {
 }

说明:加了const’的成员函数可以访问数据成员也可以什么都不做,但是一定不能修改数据成员。

6.this指针
通常来说,类内的成员函数在调用或者访问数据成员时都应该用this指针,即this->,所以在构造函数中初始化数据成员时其实正规来说也应该用this指针。

扫描二维码关注公众号,回复: 5958294 查看本文章
  1. 对象数组
    从字面可知道,对象数组就是由对象组成的数组。
    数组对象就是大批量实例化对象的一种方法,以往我们都是这样:Student stu 实例化对象,如果有好几百个属于相同类的对象应该怎么办?这时候就用到了对象数组,顾名思义,就是吧所有要实例化的对象都放到一个组里面,然后直接实例化这个组,就像这样:Student stu[100] 这样一次性实例化100个对象。stu[1] 这就是对象数组里面的一个对象,stu[1].m_iX = 10 这就是给对象的属性进行定义。

注意:对象数组立里面的对象没有具体的名字。从实例化的方式就可以看出

实例化对象数组的几种方法假设这些这些对象都来自类Student
a . Student stu【4】。这种定义方式默认四个对象都执行默认构造函数。此时stu不为对象名,而是数组名。
b. 这种方法就是用含参数构造定义四个对象

const int num = 4;
  Student stu [num] = {
              Student(14,16);  
              Student(18,16);
              Student(19,16);
              Student(20,16);
   }

c. 这种方法就是部分用含参数构造定义对象,部分用默认构造定义对象。

 const int num = 4;
      Student stu [num] = {
                  Student(14,16);  
                  Student();
                  Student(19,16);
                  Student(20,16);
       }

d. 这种方法就是开头部分用含参数构造定义对象,其余剩下部分用默认构造定义对象。

 const int num = 4;
      Student stu [num] = {
                  Student(14,16);     
       }

8.用类抽象数据类型

当我们自己定义一种数据类型时,可以用类的方法实现。比如stl里的链表、栈、堆,都是类的方法实现的。下面我们自己用类来书写栈。

先介绍栈的特点:

  • 可创建空栈
  • 可将数据从栈顶压入
  • 可将数据从栈顶弹出(删除)
  • 可以查看是否满了
  • 可以查看是否为空

分析:类的私有部分定义数据存取方式,比如数组。而共有部分的函数定义数据的操作方法,比如插入、删除。。。。。

代码:

#ifndef STACK_H_
#define STSCK_H_
#include <iostream>
using namespace std;

class stack
{
private:
    float shuzu[10];
    int top;  //the index of the top stack

public:
    stack();
    bool isempty() const ;
    bool isfull() const;

    bool pop(float &a); //get the top data to a;
    bool push(float data); //insert data

};
#endif

#include "stack.h"

stack::stack()
{
    this->top = 0;
}

bool stack::isempty() const
{
    if(this->top == 0)
        return true;
    else
        return false;
}

bool stack::isfull() const
{
    if(this->top == 10)
        return true;
    else
        return false;
}

bool stack::pop(float &a) //get the top data to a;
{
    if(this->top > 0)
    {
        this->top = this->top - 1;
        a = shuzu[this->top];
        return true;
    }

    else
        return false;
}

bool stack::push(float data) //insert data
{
    if(top < 10)
    {
        shuzu[top] = data;
        top++;
        return true;
    }

    else
        return false;
}

#include <iostream>
#include "stack.h"
using namespace std;

int main()
{
    stack sta;
    float k = 0;
    if(sta.push(1))
    {
        cout << "insert succeed" << endl;
    }
    else
    {
        cout << "insert fail" << endl;
    }

    sta.pop(k);
    cout << k << endl;

    if(!sta.isempty())
    {
        cout << "stack is not empty!" << endl;
    }
    else
       cout << "stack is  empty!" << endl;


    return 0;
}

二、函数缺省值(带有默认参数的函数)

1.通常,调用函数时,要为函数的每个参数给定对应的实参。例如:

void delay(int loops); //函数声明
    void delay(int loops) //函数定义
    {
     if(100ps==0)
      return;
      for(int i=0;i<loops,i++);
    }

无论何时调用delay()函数,都必须给loops传一个值以确定时间。但是c++提供了一种默认参数,就是当你传入了实参时,才设定参数的值为你传入的,否则参数的值为默认。形式:

void delay(int loops=1000); 



delay(2500); //loops设置为2500
delay(); //ok:loops采用默认值1000

调用中,若不给出参数,则按指定的默认值进行工作。

同理,我们可以设定多个默认参数值,如:void point(int=3,int=4);

**但是要注意:**默认参数在函数声明中提供,当又有声明又有定义时,定义中不允许默认参数。如果函数只有定义,则默认参数才可出现在函数定义中。例如:

void point(int=3,int=4); //声明中给出默认值
    void point(intx,inty) //这就是错的,因为前面有了声明,定义中不允许再给出默认值
    {
     cout <<x<<endl;
     cout <<y<<endl;
    }

正确:

void point(int=3,int=4); //声明中给出默认值
        void point() //这就是对的,因为前面有了声明,定义中不允许再给出默认值。
        {
         cout <<x<<endl;
         cout <<y<<endl;
        }

2.默认参数的顺序规定
我们定义函数时,可以同时含有默认参数和非默认参数。如:

void func(int a, int b=2,int c=3,int d=4);

但是要注意:默认参数要放在列表右边,非默认参数放在左边!!!!

比如:

void func(int a=1,int b,int c=3, int d=4); //error 
void func(int a, int b=2,int c=3,int d=4); //ok

但是在调用函数时,是从左向右匹配参数:

func(10,15,20,30); //ok:调用时给出所有实参
func(); //error:参数a没有默认值,所以只要参数列表中出现非默认参数,都要在调用时指定非默认参数的参数值
func(i2,12); //ok:参数c和d默认,a与b都为12
func(2,15,20); //ok:只有d为默认。

总结

函数缺省值不难,但是易错点在于:当又有声明又有定义时,定义中不允许默认参数。前面已经说明了。这在书写类 的时候会经常遇到,因为书写类的时候就是既有声明又有定义,而且分开写的。


三、运算符重载

1.定义:将一般的运算符运用到类与一个东西的运算中。
2.注意:想运算的两个东西中至少要有一个类,所以三种(类+数,类+类,数+类),这里准确来说应该是对象的运算,为了方便理解我写成类
3.书写方法:

void operator+ (int a,Stu &b)
{
	cout《 (a+b.c);
}
说明:
1.可以有返回值,也可以没有返回值,可以返回一个类,此时要写成Stu operator+ (int a,Stu &b),即前面改为类名,也可以用引用:Stu& operator+ (int a,Stu &b);
2.要重载的运算符写在operator后面
3.参数列表中参数的顺序和类型要和调用该重载运算符时二者的顺序一样,比如这里为int+Stu,用的时候也必须为int+Stu,不能为Stu+int
4.当有返回类型时,可以实现连续运算,

4.运算符重载可以写在类里面作为一种成员函数,但是此时该运算符只能用于该类+另外一个参数,也就是指定了运算符左边的对象只能为该类,此时就只只需写一个参数即可,比如:

int Stu::operator+ (int a)
{
	return (this->age +a);
}

5.当一个类作为参数传入函数时,可以写成: void operator+ (int a,Stu &b),也可以: void operator+ (int a,Stu b),只不过前者用了引用,速度效率更快,其他没有什么区别。

6.实例
原先如果我们想在一个类里面写一个实现该类和另一个类的相加的函数时,这样写:

Time Time::sum(Time &t)
{
	Time sum;
	sum.minute = minutes+ t.minutes;
	return sum;//这里就是返回一个类
}

这样我们使用时就是:

Time coding;
Time fixing;
Time total;
total = coding.sum(fixing);

而如果我们用了运算符重载(与前面比较就只是把函数名变成了operator+):

 Time Time::operator+(Time &t)
    {
    	Time sum;
    	sum.minute = minutes+ t.minutes;
    	return sum;//这里就是返回一个类
    }

使用时:1.因为operator+本质上是一个成员函数,所以可以用 total = coding.operator+ (fixing);
2.或者直接用:total = coding+ fixing;

7.其他的运算符重载
首先需要介绍一下运算符,运算符分为:1元、2元,3元运算符,+ - 就属于2元,++ — !这类属于一元

重载>= ,<=

int operator >= (Stu &a , Stu &b)
{
	return (a.age >= b.age);  //注意:这里的》=为系统默认的,它用来比较数值,为我们而我们重载的是用来比较对象的
}

重载&

int operator & (Stu &a , Stu &b)
    {
    	return (a.age & b.age);  //注意:这里的&为系统默认的,它用来比较数值,为我们而我们重载的是用来比较对象的
    }

重载-

 int operator - (Stu &a)
    {
    	return (-a.age );  //注意:这里的&为系统默认的,它用来比较数值,为我们而我们重载的是用来比较对象的
    }

四、友元

前面我们提到过,类的私有数据成员只有类的公有成员函数和友元可以访问,不能通过实例化直接访问。
而这里的友元包括三类:

  • 友元函数:一个函数是这个类的友元
  • 友元类:另一个类是这个类的友元
  • 友元成员函数:另一个类的一个函数是这个类的友元

这里我们先学习友元函数

如果要声明函数为一个类的友元,需要在类定义中该函数原型前使用关键字 friend,如下所示:

class Box
{
private:
   double width;
public:
   double length;
   friend void printWidth( Box box ); //这里就是
   void setWidth( double wid );
};

请看下面的程序的实现:

#include <iostream>
 
using namespace std;
 
class Box
{
   double width;
public:
   friend void printWidth( Box box );
   void setWidth( double wid );
};

// 成员函数定义
void Box::setWidth( double wid )
{
    width = wid;
}

// 请注意:printWidth() 不是任何类的成员函数
void printWidth( Box box )
{
   /* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
   cout << "Width of box : " << box.width <<endl;
}
 
// 程序的主函数
int main( )
{
   Box box;
 
   // 使用成员函数设置宽度
   box.setWidth(10.0);
   
   // 使用友元函数输出宽度
   printWidth( box );
 
   return 0;
}

说明:

  1. 类的友元函数声明要写在类里面,开头加上friend。定义要写在类外,因为友元函数不是成员函数,所以在定义时不能用::。定义的时候就是把friend去掉即可

  2. 类的友元函数虽然不是成员函数,但是可以访问类的私有数据成员,具体操作就是当把这个类的对象作为参数传入这个友元函数时,在友元函数内部可以用实例化对象的方式直接访问私有数据成员。比如下面的box.width,而这在前面说过是不能通过实例化的方式来访问,而现在可以了:

    void printWidth( Box box )
    {
    /* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
    cout << "Width of box : " << box.width <<endl;
    }

猜你喜欢

转载自blog.csdn.net/zouxu634866/article/details/88245220