【C++入门】C++类和对象

本文详细介绍了C++中的类和对象,包括对象的定义、成员变量和成员函数的使用,以及构造函数、复制构造函数、类型转换构造函数的概念和应用。此外,还探讨了析构函数、静态成员、友元和常量成员函数等核心概念,强调了这些特性在实际编程中的重要性。
摘要由CSDN通过智能技术生成

C++类&对象

在这里插入图片描述
在这里插入图片描述

一、类和对象定义

1.对象

  • 定义

    通过类,可以定义变量。类定义出来的变量,也称为类的实例

  • 对象的内存分配

    和结构变量一样,对象所占用的内存空间的大小,等于所有成员变量的大小之和。
    每个对象各有自己的存储空间。一个对象的某个成员变量被改变了,不会影响到另一个对象。

  • 对象间的运算

    和结构变量一样,对象之间可以用 “=”进行赋值,但是不能用 “==”,“!=”,“>”,“<”“>=”“<=”进行比较,除非这些运算符经过了“重载”。

2.类

  • 定义:从客观事物抽象出类

    类是 C++ 的核心特性,通常被称为用户定义的类型。
    类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。

  • 示例:矩形类

    矩形的属性就是长和宽。因此需要两个变量,分别代表长和宽。
    矩形可以有设置长和宽,算面积,和算周长这三种行为。
    将长、宽变量和设置长,宽,求面积,以及求周长的三个函数“封装”在一起,就能形成一个“矩形类”。

 class CRectangle 
  { 
  	public: 
  	int w, h; 
  	int Area() { 
  		return w * h; 
  	} 
  	int Perimeter(){ 
  		return 2 * ( w + h); 
  } 
  void Init( int w_,int h_ ) { 
 	 w = w_; h = h_; 
  } 
  }; //必须有分号

二、成员变量&成员函数

1.使用类的成员变量和成员函数

  • 用法1:对象名.成员名
  CRectangle r1,r2; 
  r1.w = 5; 
  r2.Init(5,4);
  • 用法2. 指针->成员名
  CRectangle r1,r2; 
  CRectangle * p1 = & r1; 
  CRectangle * p2 = & r2; 
  p1->w = 5; 
  p2->Init(5,4); //Init作用在p2指向的对象上
  • 用法3:引用名.成员名
  CRectangle r2; 
  CRectangle & rr = r2; 
  rr.w = 5; 
  rr.Init(5,4); //rr的值变了,r2的值也变

2.类成员的可访问范围

  • 访问修饰符

    类成员的访问限制是通过在类主体内部对各个区域标记 public、private、protected 来指定的。关键字 public、private、protected 称为访问修饰符。

    • 公有(public)成员

      公有成员在程序中类的外部是可访问的。可以不使用任何成员函数来设置和获取公有变量的值

    • 私有(private)成员(无关键字默认)

      私有成员变量或函数在类的外部是不可访问的,甚至是不可查看的。只有类和友元函数可以访问私有成员。
      默认情况下,类的所有成员都是私有的。

    • protected(受保护)成员

      受保护成员变量或函数与私有成员十分相似,但有一点不同,protected(受保护)成员在派生类(即子类)中是可访问的

  • 成员函数

    • 成员函数内部

      • 当前对象的全部属性、函数
      • 同类其它对象的全部属性、函数
    • 成员函数外部

      • 只能够访问该类对象的公有成员
  • 示例

class CEmployee
{
private:
    char szName[30]; //名字
public:
    int salary; //工资
    void setName(char *name);
    void getName(char *name);
    void averageSalary(CEmployee e1, CEmployee e2);
};
void CEmployee::setName(char *name)
{
    strcpy(szName, name); //ok
}
void CEmployee::getName(char *name)
{
    strcpy(name, szName); //ok
}
void CEmployee::averageSalary(CEmployee e1,
                              CEmployee e2)
{
    cout << e1.szName; //ok,访问同类其他对象私有成员
    salary = (e1.salary + e2.salary) / 2;
}
int main()
{
    CEmployee e;
    strcpy(e.szName, "Tom1234567889"); //编译错,不能访
    问私有成员
    e.setName("Tom"); // ok
    e.salary = 5000;  //ok
    return 0;
}
int main()
{
    CEmployee e;
    strcpy(e.szName, "Tom1234567889"); //编译错,不能访
    问私有成员
    e.setName("Tom"); // ok
    e.salary = 5000;  //ok
    return 0;
}
  • 隐藏

    设置私有成员的机制,叫“隐藏”
    “隐藏”的目的是强制对成员变量的访问一定要通过成员函数进行,那么以后成员变量的类型等属性修改后,只需要更改成员函数即可。否则,所有直接访问成员变量的语句都需要修改。

3.成员函数可以重载及参数缺省

  • 成员函数重载
#include <iostream>
using namespace std;
class Location
{
private:
    int x, y;

public:
    void init(int x = 0, int y = 0);
    void valueX(int val) { x = val; }
    int valueX() { return x; }
};
  • 成员函数缺省
class Location
{
private:
    int x, y;

public:
    void init(int x = 0, int y = 0);
    void valueX(int val = 0) { x = val; }
    int valueX() { return x; }
};
Location A;
A.valueX(); //错误,编译器无法判断调用哪个valueX

三、构造函数

1.构造函数概念

  • 成员函数的一种
  • 一个类可以有多个构造函数

2.构造函数定义

  • 名字与类名相同,可以有参数,不能有返回值(void也不行)

3.构造函数生成

  • 如果定义类时没写构造函数,则编译器生成一个默认的无参数的构造函数,默认构造函数无参数,不做任何操作
  • 如果定义了构造函数,则编译器不生成默认的无参数的构造函数
  • 对象生成时构造函数自动被调用。对象一旦生成,就再也不能在其上执行构造函数

4.构造函数作用

  • 作用是对对象进行初始化,如给成员变量赋初值
  • 构造函数执行必要的初始化工作,有了构造函数,就不必专门再写初始化函数,也不用担心忘记调用初始化函数。
  • 有时对象没被初始化就使用,会导致程序出错。

5.构造函数示例

  • 编译器自动生成
class Complex
{
private:
    double real, imag;

public:
    void Set(double r, double i);
};                         //编译器自动生成默认构造函数
Complex c1;                //默认构造函数被调用
Complex *pc = new Complex; //默认构造函数被调用
  • 可以有多个构造函数,参数个数或类型不同
class Complex
{
private:
    double real, imag;

public:
    void Set(double r, double i);
    Complex(double r, double i);
    Complex(double r);
    Complex(Complex c1, Complex c2);
};
Complex::Complex(double r, double i)
{
    real = r;
    imag = i;
}
Complex::Complex(double r)
{
    real = r;
    imag = 0;
}
Complex::Complex(Complex c1, Complex c2);
{
    real = c1.real + c2.real;
    imag = c1.imag + c2.imag;
}
Complex c1(3), c2(1, 0), c3(c1, c2);
// c1 = {3, 0}, c2 = {1, 0}, c3 = {4, 0};
  • 构造函数最好是public的,private构造函数不能直接用来初始化对象
class CSample
{
private:
    CSample()
    {
    }
};
int main()
{
    CSample Obj; //err. 唯一构造函数是private
    return 0;
}

四、复制构造函数

1.复制构造函数基本概念

  • 只有一个参数,即对同类对象的引用
  • 形如 X::X( X& )或X::X(const X &)
  • 如果没有定义复制构造函数,那么编译器生成默认复制构造函数。默认的复制构造函数完成复制功能
  • 如果定义的自己的复制构造函数,则默认的复制构造函数不存在。
  • 不允许有形如 X::X( X )的构造函数

2.复制构造函数起作用的三种情况

  • 1)当用一个对象去初始化同类的另一个对象时。
  Complex c2(c1); 
  Complex c2 = c1; //初始化语句,非赋值语句
  • 2)如果某函数有一个参数是类 A 的对象,那么该函数被调用时,类A的复制构造函数将被调用。
class A
{
public:
    A(){};
    A(A &a)
    {
        cout << "Copy constructor called" << endl;
    }
};
void Func(A a1) {}
int main()
{
    A a2;
    Func(a2);
    return 0;
}
//程序输出结果为: Copy constructor called
    1. 如果函数的返回值是类A的对象时,则函数返回时,A的复制构造函数被调用:
class A
{
public:
    int v;
    A(int n) { v = n; };
    A(const A &a)
    {
        v = a.v;
        cout << "Copy constructor called" << endl;
    }
};
A Func()
{
    A b(4);
    return b;
}
int main()
{
    cout << Func().v << endl;
    return 0;
}
//输出结果:
//Copy constructor called
//4

3.注意:对象间赋值并不导致复制构造函数被调用

五、类型转换构造函数

1.类型转换构造函数基本概念

  • 定义转换构造函数的目的是实现类型的自动转换。
  • 只有一个参数,而且不是复制构造函数的构造函数,一般就可以看作是转换构造函数。
  • 当需要的时候,编译系统会自动调用转换构造函数,建立一个无名的临时对象(或临时变量)。

2.类型转换构造函数示例

class Complex
{
public:
    double real, imag;
    Complex(int i)
    { //类型转换构造函数
        cout << "IntConstructor called" << endl;
        real = i;
        imag = 0;
    }
    Complex(double r, double i)
    {
        real = r;
        imag = i;
    }
};
int main()
{
    Complex c1(7, 8);
    Complex c2 = 12;
    c1 = 9; // 9被自动转换成一个临时Complex对象
    cout << c1.real << "," << c1.imag << endl;
    return 0;
}

六、析构函数

1.析构函数基本概念

  • 名字与类名相同,在前面加‘~’, 没有参数和返回值,一个类最多只能有一个析构函数。
  • 析构函数对象消亡时即自动被调用。可以定义析构函数来在对象消亡前做善后工作,比如释放分配的空间等。
  • 如果定义类时没写析构函数,则编译器生成缺省析构函数。缺省析构函数什么也不做。
  • 如果定义了析构函数,则编译器不生成缺省析构函数。

2.析构函数示例

class String
{
private:
    char *p;

public:
    String()
    {
        p = new char[10];
    }
    ~String();
};
String ::~String()
{
    delete[] p;
}

七、this指针

1.this指针的作用

  • 非静态成员函数中可以直接使用this来代表指向该函数 作用的对象的指针。
  class Complex
  {
  public:
      double real, imag;
      void Print() { cout << real << "," << imag; }
      Complex(double r, double i) : real(r), imag(i)
      {
      }
      Complex AddOne()
      {
          this->real++;  //等价于 real ++;
          this->Print(); //等价于 Print
          return *this;
      }
  };

2.this指针和静态成员函数

  • 静态成员函数中不能使用 this 指针

    因为静态成员函数并不具体作用与某个对象

八、静态成员

1.静态成员基本概念

  • 在定义前面加了static关键字的成员
  class CRectangle
  {
  private:
      int w, h;
      static int nTotalArea; //静态成员变量
      static int nTotalNumber;
  
  public:
      CRectangle(int w_, int h_);
      ~CRectangle();
      static void PrintTotal(); //静态成员函数
  }
  • 静态成员&普通成员

    • 普通成员变量每个对象有各自的一份,而静态成员变 量一共就一份,为所有对象共享。
  //sizeof 运算符不会计算静态成员变量。
  class CMyclass
  {
      int n;
      static int s;
  };
  //则 sizeof( CMyclass ) 等于 4
- 普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。

2.静态成员的访问

静态成员不需要通过对象就能访问

    1. 类名::成员名
  CRectangle::PrintTotal();
    1. 对象名.成员名
  CRectangle r; r.PrintTotal();
    1. 指针->成员名
  CRectangle * p = &r; p->PrintTotal();
    1. 引用.成员名
  CRectangle & ref = r; int n = ref.nTotalNumber;

3.静态成员注意事项

  • 在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数。

九、成员对象和封闭类

1.封闭类定义

有成员对象的类叫 封闭(enclosing)类

class CTyre //轮胎类
{
private:
    int radius; //半径
    int width;  //宽度
public:
    CTyre(int r, int w) : radius(r), width(w) {}
};
class CEngine //引擎类
{
};
class CCar
{ //汽车类
private:
    int price; //价格
    CTyre tyre;
    CEngine engine;

public:
    CCar(int p, int tr, int tw);
};
CCar::CCar(int p, int tr, int w) : price(p), tyre(tr, w){};
int main()
{
    CCar car(20000, 17, 225);
    return 0;
}

2.封闭类构造函数和析构函数的执行顺序

  • 封闭类对象生成时,先执行所有对象成员的构造函数,然后才执行封闭类的构造函数。
  • 对象成员的构造函数调用次序和对象成员在类中的说明次序一致,与它们在成员初始化列表中出现的次序无关。
  • 当封闭类的对象消亡时,先执行封闭类的析构函数,然后再执行成员对象的析构函数。次序和构造函数的调用次序相反。

3.封闭类实例

class CTyre
{
public:
    CTyre() { cout << "CTyre contructor" << endl; }
    ~CTyre() { cout << "CTyre destructor" << endl; }
};
class CEngine
{
public:
    CEngine() { cout << "CEngine contructor" << endl; }
    ~CEngine() { cout << "CEngine destructor" << endl; }
};
class CCar
{
private:
    CEngine engine;
    CTyre tyre;

public:
    CCar() { cout << “CCar contructor” << endl; }
    ~CCar() { cout << "CCar destructor" << endl; }
};

4.封闭类的复制构造函数

封闭类的对象,如果是用默认复制构造函数初始化的,那么它里面包含的成员对象, 也会用复制构造函数初始化。

十、友元

1.友元函数

  • 一个类的友元函数可以访问该类的私有成员
  class CCar; //提前声明 CCar类,以便后面的CDriver类使用
  class CDriver
  {
  public:
      void ModifyCar(CCar *pCar); //改装汽车
  };
  class CCar
  {
  private:
      int price;
      friend int MostExpensiveCar(CCar cars[], int total); //声明友元
      friend void CDriver::ModifyCar(CCar *pCar);          //声明友元
  };
  友元(friends, P199) void CDriver::ModifyCar(CCar *pCar)
  {
      pCar->price += 1000; //汽车改装后价值增加
  }
  int MostExpensiveCar(CCar cars[], int total)
  //求最贵汽车的价格
  {
      int tmpMax = -1;
      for (int i = 0; i < total; ++i)
          if (cars[i].price > tmpMax)
              tmpMax = cars[i].price;
      return tmpMax;
  }
  int main()
  {
      return 0;
  }

2.友元类

  • 如果A是B的友元类,那么A的成员函数可以访问B的私有成员
  class CCar
  {
  private:
      int price;
      friend class CDriver; //声明CDriver为友元类
  };
  class CDriver
  {
  public:
      CCar myCar;
      void ModifyCar()
      {                        //改装汽车
          myCar.price += 1000; //因CDriver是CCar的友元类,
          //故此处可以访问其私有成员
      }
  };
  int main() { return 0; }

3.友元类注意事项

友元类之间的关系不能传递,不能继承

十一、常量成员函数

如果一个成员函数中没有调用非常量成员函数 ,也没有修改成员变量的值,那么,最好将其 写成常量成员函数。

1.常量成员函数基本概念

  • 在定义常量成员函数和声明常量成员函数时都应该使用const 关键字
class Sample
{
private:
    int value;

public:
    void PrintValue() const;
};
void Sample::PrintValue() const
{ //此处不使用const会
    //导致编译出错
    cout << value;
}
void Print(const Sample &o)
{
    o.PrintValue(); //若 PrintValue非const则编译错
}
  • 常量成员函数内部不能改变属性的值,也不能调用非常量成员函数
class Sample
{
private:
    int value;

public:
    void func(){};
    Sample() {}
    void SetValue() const
    {
        value = 0; // wrong
        func();    //wrong
    }
};
const Sample Obj;
Obj.SetValue(); //常量对象上可以使用常量成员函数

2.常量成员函数的重载

  • 两个函数,名字和参数表都一样,但是一个是const,一个不是,算重载。
  int main()
  {
      const CTest objTest1;
      CTest objTest2;
      cout << objTest1.GetValue() << "," << objTest2.GetValue();
      return 0;
  }
  //= > 1, 2

3.mutable成员变量

  • 可以在const成员函数中修改的成员变量
  class CTest
  {
  public:
      bool GetData() const
      {
          m_n1++;
          return m_b2;
      }
  
  private:
      mutable int m_n1;
      bool m_b2;
  };

【知识索引】【C++入门】

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

BkbK-

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

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

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

打赏作者

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

抵扣说明:

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

余额充值