c++快速入门7:类和对象A:封装与初始化

创建类和对象

本章介绍C++面向对象编程的前两条原则:封装和继承

封装数据

类是一个数据结构,它同时包含变量和函数。这些被统称为 "成员",而函数被称为 "方法"。

外部对类成员的访问是由类声明中的 "访问符 "控制的。通常情况下,这些参数会拒绝对变量成员的访问,但允许对变量成员存储和获取数据方法的访问。这种 "数据隐藏 "技术确保了存储的数据被安全地封装在类的变量成员中,这也是面向对象编程(OOP:Object Oriented Programming)的首要原则。

类的声明以类的关键字开始,后面是空格,然后是程序员指定的名字--遵守通常的C++命名惯例,但以大写字母开始。接下来,是访问符和类成员,包含在一对大括号中。每个类的声明都必须在大括号后用分号结束--所以类的声明语法看起来像这样。

class ClassName
{
  access specifier :
    member1 ;
    member2 ;
  access specifier :
  member3 ;
  member4 ;
} ;

访问指定符可以是public、private或protected。

  • public可以从类可见的任何地方访问。
  • private只能被同一类别的其他成员访问
  • protected的成员只能被同一类的其他成员和从该类派生的类的成员所访问。

默认情况下,所有的类成员为private。

任何现实世界的对象都可以通过其属性和动作来定义。例如狗有诸如年龄、体重和颜色等属性,以及它可以执行的动作,如吠叫。C++中的类机制提供了在程序中创建虚拟狗对象的方法,类中的变量成员可以代表它的属性,而方法代表它的动作。

class Dog
{
  private: // The default access level.
    int age, weight ;
    string color ;
  public :
  void bark() ;
    // ... Plus methods to store/retrieve data.
} ;

类不能完美地模拟实际对象,但其目的是为了封装所有相关的属性和动作。
重要的是要认识到,类的声明只是定义了数据结构,为了创建一个对象,你必须声明该数据结构的 "实例"。这与常规C++数据类型的实例声明方式相同。

另外,可以通过在类声明的结束括号和最后的分号之间指定其名称来创建实例对象。通过指定一个以逗号分隔的对象名称列表,可以用这种方式创建多个实例。例如,下面列出的类声明创建了四个狗类的实例对象,分别命名为

class Dog
{
  int age, weight ;
  string color ;
  public:
    void bark() ;
      // ... Plus methods to store/retrieve data.
} fido, pooch, rex, sammy ;

传统上,类名以大写字母开头,对象名以小写字母开头。

创建单个对象

为了从类的私有成员中分配和获取数据,必须在类中添加特殊的公共访问器方法。这些方法是 "setter "方法,用来分配数据,"getter "方法,用来检索数据。访问器方法通常被命名为它们所针对的变量,第一个字母为大写,并分别以 "set "或 "get "为前缀。例如,处理年龄变量的访问器方法可以被命名为setAge()和getAge()。

#include <string>
#include <iostream>
using namespace std;

class Dog
{
  int age, weight ;
  string color ;

  public:
    void bark() { cout << "WOOF!" << endl ; }

    void setAge( int yrs ) { age = yrs ; }
    void setWeight( int lbs ) { weight = lbs ; }
    void setColor( string hue ) { color = hue ; }
    
    int getAge() { return age; }
    int getWeight() { return weight; }
    string getColor() { return color; }
} ;

int main()
{
  Dog fido ;

  fido.setAge( 3 ) ;
  fido.setWeight( 15 ) ;
  fido.setColor( "brown" ) ;

  cout << "Fido is a " << fido.getColor() << " dog" << endl ;
  cout << "Fido is " << fido.getAge() << " years old" << endl ;
  cout << "Fido weighs " << fido.getWeight() << " pounds" << endl ;

  fido.bark() ;

  return 0 ;
}

创建多个对象

前面例子中的类声明包含了只有一行的短方法,这些方法是 "内联 "创建的--完全在类声明块中。当方法超过两行时,它们不应该被内联创建,而应该在类声明块中作为 "原型 "声明,并在类声明之后单独定义。该定义必须在方法名前加上类名和范围解析操作符::,以确定包含其原型的类。

//=============================================
// C++ Programming in easy steps 5ed. [7:120]
//=============================================

#include <string>
#include <iostream>
using namespace std;

class Dog
{
  int age, weight ;
  string color ;

public:
  void bark() { cout << "WOOF!" << endl ; }

  //void setAge( int yrs ) { age = yrs ; }
  //void setWeight( int lbs ) { weight = lbs ; }
  //void setColor( string hue ) { color = hue ; }

  void setValues( int, int, string ) ; // Prototype.

  int getAge() { return age; }
  int getWeight() { return weight; }
  string getColor() { return color; }
} ;

void Dog::setValues( int age, int weight, string color ) // Definition.
{
  this -> age = age ;
  this -> weight = weight ;
  this -> color = color ;
}

int main()
{
  Dog fido ;

  //fido.setAge( 3 ) ;
  //fido.setWeight( 15 ) ;
  //fido.setColor( "brown" ) ;

  fido.setValues( 3, 15, "brown" ) ;

  cout << "Fido is a " << fido.getColor() << " dog" << endl ;
  cout << "Fido is " << fido.getAge() << " years old" << endl ;
  cout << "Fido weighs " << fido.getWeight() << " pounds" << endl ;

  fido.bark() ;

  Dog pooch ;
  pooch.setValues( 4, 18, "gray" ) ;
  cout << "Pooch is a " << pooch.getAge() ;
  cout << " year old " << pooch.getColor() ;
  cout << " dog who weighs " << pooch.getWeight() ;
  cout << " pounds." ;
  pooch.bark() ; 

  return 0 ;
}

this->类指针可以用来明确地指向该类成员。例如,this->age指的是类成员变量,而age指的是参数。

初始化类成员

类的变量成员可以通过 "构造器 "方法来初始化,这个方法在创建类的实例时被调用。构造函数方法的名字总是和类的名字完全一样,并且需要参数来设置类变量的初始值。

当构造函数方法被声明时,对应的 "析构函数 "方法也应该被声明--当类实例被销毁时调用。解构器方法总是被命名为类的名称,前缀为~。

构造器和析构器方法没有返回值,并且是自动调用的--它们不能被显式调用。

#include <string>
#include <iostream>
using namespace std;

class Dog
{
  int age, weight ;
  string color ;

  public:
    void bark() { cout << "WOOF!" << endl ; }

    Dog( int, int, string ) ;
    ~Dog() ;
    
    int getAge() { return age; }
    int getWeight() { return weight; }
    string getColor() { return color; }
} ;

Dog::Dog( int age, int weight, string color )
{
  this -> age = age ;
  this -> weight = weight ;
  this -> color = color ;
}

Dog::~Dog()
{
  cout << "Object destroyed." << endl ;
}

int main()
{
  Dog fido( 3, 15, "brown" ) ;

  //fido.setValues( 3, 15, "brown" ) ;
  
  cout << "Fido is a " << fido.getColor() << " dog" << endl ;
  cout << "Fido is " << fido.getAge() << " years old" << endl ;
  cout << "Fido weighs " << fido.getWeight() << " pounds" << endl ;

  fido.bark() ;

  Dog pooch ( 4, 18, "gray" ) ;

  //pooch.setValues ;

  cout << "Pooch is a " << pooch.getAge() ;
  cout << " year old  " << pooch.getColor() ;
  cout << " dog who weighs " << pooch.getWeight() ;
  cout << " pounds." ;
  
  pooch.bark() ;

  return 0 ;
}

尽管变量成员的初始值是由构造函数设置的,但可以添加setter方法来随后改变这些值--而这些新值可以由getter方法检索。

推荐阅读更多精彩内容