21天学C++(六)面向对象编程

完整目录、平台简介、安装环境及版本:参考《21天学C++ 概览》

六、面向对象编程

6.1 类和对象

6.1.1 创建变量

可获得如下信息

  • 变量所占内存的大小;
  • 变量可存储什么样的信息;
  • 可以对变量执行什么样的操作。

6.1.2

由若干变量(变量常常具有不同类型)及相关函数组成

  • 一个类可以由各种类型的变量组成,也可以由其他类组成;
  • 成员变量(数据):类中的变量;
  • 成员函数(方法):类中的函数,通常用来处理成员变量;

如汽车类:

  • 将汽车相关的一切都封装在一个类中,一切都在一个地方,容易引用、赋值和处理数据;
  • 使用该类时,直接使用类对象,而不用关系内部是如何工作的;

6.1.3 声明一个类: Cat

  • 使用关键字class,后跟一个{号,然后列出该类的成员变量和成员函数;
  • 声明类并没有为Cat分配内存;
  • 告诉编译器Cat是什么,包含哪些成员变量和成员函数;
  • 告诉编译器Cat有多大,如int占4个字节,则Cat占8个字节,成员函数本身不占用空间。

建议:首字母小写的方式表示变量,首字母大写的方式表示函数,风格没有标准规定,只要用一个统一的方式即可,便于互相阅读。

class Cat
{
    public unsigned int  itsAge;
    public unsigned int  itsWeight;
    public void Meow();
};

6.1.4 定义一个对象

  • 定义一个变量: unsigned int itsAge;
  • 定义一个对象:Cat Frisky;

6.1.5 类和对象的关系

对象是类个体的实例。

6.2 访问类

6.2.1 访问类成员

使用运算符(.)来访为成员变量和成员函数,如

Cat Frisky;
Frisky.itsWeight = 5;
Frisky.Meow();

6.2.2 给对象赋值,而不能给类型赋值

Cat Frisky;
Frisky.itsWeight = 5; //可以
Cat.itsWeight = 5; //不可以

6.2.3 私有private和公有public

  • 类的数据和方法默认情况下是私有private;
  • 私有成员private只能在类本身的方法内访问;
  • 公有成员public则可以被该类的所有对象访问;
  • 如类Cat1中方法Frisky.itsAge = 5; 是错误的,不能用类对象访问类私有变量;
  • Frisky对象自己能访问私有成员(如通过成员函数),但是你不能用Frisky调用私有成员,而是可以通过访问公有成员函数来访问私有成员。
class Cat1
{
    int itsAge;         
    int itsWeight;      
};       
int main()
{
   Cat1 Frisky;
   Frisky.itsAge = 5; 
   return 0;
}

类Cat2方法Frisky.itsAge = 5; 是正确的,可以用类对象访问公有成员。

class Cat2
{
  public:               
    int itsAge;         
    int itsWeight;      
};   
int main()
{
   Cat2 Frisky;
   Frisky.itsAge = 5; 
   return 0;
}
#include <iostream>   

class Cat               // declare the Cat class
{
public:               // members which follow are public
	int itsAge;         // member variable
	int itsWeight;      // member variable
};         // note the semicolon

int main()
{
	Cat Frisky;
	Frisky.itsAge = 5;    // assign to the member variable
	std::cout << "Frisky is a cat who is " ;
	std::cout << Frisky.itsAge << " years old.\n";
	return 0;
}

执行结果如下:

Frisky is a cat who is 5 years old.
请按任意键继续. . .

6.2.4 成员数据私有

应保持类成员变量为私有private,通过创建公有成员函数public来设置和获取私有成员数据;

将存储细节和数据的使用分开,如返回Cat的体重,如果希望改变体重返回时的单位,如原来是克,现在变成千克,只需修改体重对应的公有成员函数即可;

程序更容易维护。

class Cat
{
public:
	// public accessors
	unsigned int GetAge(){return itsAge;}
	void SetAge(unsigned int Age){itsAge = Age;}

	unsigned int GetWeight(){return itsWeight;}
	void SetWeight(unsigned int Weight){itsWeight = Weight;}

	// public member functions
	void Meow();

	// private member data
private:
	unsigned int  itsAge;
	unsigned int  itsWeight;
};

int main()
{
	Cat Frisky;
	Frisky.SetAge(5);
	return 0;
}

执行结果如下:

6.2.5 私有性与安全性

将方法或数据声明为私有能使编译器发现你的错误,这些错误以后可能成为程序的bug,如误修改等。

class Cat
{
public:
	void Meow();
	unsigned int  itsAge;
	unsigned int  itsWeight;
};
int main()
{
	Cat Frisky;
	Frisky.itsAge = 5;
	Frisky.itsWeight = 8;
	Frisky.Meow();
	return 0;
}
class Car
{
public:
	void Start();
	void Accelerate();
	void Brake();
	void SetYear(int year);
	int GetYear();
private:
	int year;
};
int main()
{
	Car OldFaithful;
	OldFaithful.SetYear(8);
	int bought = OldFaithful.GetYear();
	OldFaithful.Start();
	return 0;
}

6.3 实现类方法

访问函数为类的私有成员数据提供一种公有界面,每个访问函数以及你所声明的其他类方法都必须有一个实现方法,该方法称为函数定义。  

#include <iostream>         // for cout

class Cat                   // begin declaration of the class
{
public:                   // begin public section
	int GetAge();           // accessor function
	void SetAge (int age);  // accessor function
	void Meow();            // general function
private:                  // begin private section
	int itsAge;             // member variable
};

int Cat::GetAge()
{
	return itsAge;
}

void Cat::SetAge(int age)
{
	// set member variable itsAge to
	// value passed in by parameter age
	itsAge = age;
}

void Cat::Meow()
{
	std::cout << "Meow.\n";
}

int main()
{
	Cat Frisky;
	Frisky.SetAge(5);
	Frisky.Meow();
	std::cout << "Frisky is a cat who is " ;
	std::cout << Frisky.GetAge() << " years old.\n";
	Frisky.Meow();
	return 0;
}

执行结果如下:

Meow.
Frisky is a cat who is 5 years old.
Meow.
请按任意键继续. . .

6.4 实现类方法

6.4.1 类初始化

把变量的定义和赋初值结合起来

6.4.2 类初始化

  • 与类同名的类方法;
  • 可以根据需要带参数,但是不能有返回值,返回值为空也不行;

6.4.3 类初始化

  • 与类同名的类方法;
  • 函数前面有一个波浪号~;
  • 没有参数,也没有返回值;

6.4.4 类初始化

  • 如果没有声明构造函数或析构函数,编译器会自动创建一个默认构造函数和默认析构函数;
  • 默认构造函数:没有参数的构造函数,不进行任何操作;
  • 默认析构函数:没有参数,不进行任何动作;

6.4.5 所有对象都必须构造和析构

  • Cat Frisky;//默认构造函数
  • Cat Frisky(3);//一个参数的构造函数
  • Cat Frisky(3,5);//两个参数的构造函数

一旦定义了构造函数,系统将不再提供默认构造函数;

class Cat{
  public:                  
    Cat(int initialAge);   
    ~Cat();            
};
Cat::Cat(int initialAge){
   itsAge = initialAge;
}
Cat::~Cat(){
}
int main(){
   Cat Frisky;//没有合适的默认构造函数可用
   Cat Frisky(5);
   return 0;
}

执行结果如下:

1>main.cpp
1>e:\train\source\c++\06004_default\main.cpp(12) : error C2065: “itsAge”: 未声明的标识符
1>e:\train\source\c++\06004_default\main.cpp(17) : error C2512: “Cat”: 没有合适的默认构造函数可用
1>e:\train\source\c++\06004_default\main.cpp(18) : error C2374: “Frisky”: 重定义;多次初始化
1>        e:\train\source\c++\06004_default\main.cpp(17) : 参见“Frisky”的声明

6.5 成员函数const

将类方法声明为const,则说明该函数不会改变该类的任何一个成员变量的值,如:

  • int GetAge() const;//只是返回age值,不对age做修改;
  • void SetAge(int age) const;//错误,该函数会修改成员变量age值;

注:编程时应尽可能使用const,借助编译器捕捉错误,而不至于让这些错误运行时成为bug

class Cat{
  public:
    Cat(int initialAge);
    ~Cat();
    int GetAge() const;         
    void SetAge (int age);
  private:
    int itsAge;
};
Cat::Cat(int initialAge){
   itsAge = initialAge;
}
Cat::~Cat(){
}
int Cat::GetAge() const{
   return (itsAge++);         //const函数不能修改成员变量
}
void Cat::SetAge(int age){
   itsAge = age;
}
int main(){
   Cat Frisky;                 // 构造函数不匹配
   Frisky.Meow();          // 未声明该函数
   Frisky.Bark();              // 未声明该函数
   Frisky.itsAge = 7;          // 不能直接访问私有成员
   return 0;
}

6.6 *.h *.cpp

将类声明放在.h文件,将类方法的定义放在.cpp文件

  • 将声明和定义分开,一个.h文件可以同时被多个.cpp文件调用;
  • 类声明告诉编译器这个类是什么,持有什么样的数据,拥有什么样的函数,同时告诉用户如何与类打交道;
  • 函数定义告诉编译器该函数的功能是什么。
#include <iostream>
class Cat
{
public:
	Cat (int initialAge);
	~Cat();
	int GetAge() const { return itsAge;}       // inline!
	void SetAge (int age) { itsAge = age;}       // inline!
	void Meow() const  { std::cout << "Meow.\n";}  // inline!
private:
	int itsAge;
};
#include "Cat.h"  

Cat::Cat(int initialAge)   //constructor
{
	itsAge = initialAge;
}

Cat::~Cat()             //destructor, takes no action
{
}

// Create a cat, set its age, have it
// meow, tell us its age, then meow again.
int main()
{
	Cat Frisky(5);
	Frisky.Meow();
	std::cout << "Frisky is a cat who is " ;
	std::cout << Frisky.GetAge() << " years old.\n";
	Frisky.Meow();
	Frisky.SetAge(7);
	std::cout << "Now Frisky is " ;
	std::cout << Frisky.GetAge() << " years old.\n";
	return 0;
}

执行结果如下:

Meow.
Frisky is a cat who is 5 years old.
Meow.
Now Frisky is 7 years old.
请按任意键继续. . .

6.7 内联函数

6.7.1 方法1:在返回类型前添加inline关键字

inline int Cat::GetAge()
{
   return itsAge;
}

6.7.2 方法2:将函数定义放到类声明中

class Cat
{
  public:
    int GetAge() const { return itsAge;}       // inline!
  private:
    int itsAge;
};

6.8 类作为成员变量

声明简单类并将它们包含在较复杂的类声明中

  • 如汽车类,包含车轮类、发动机类、离合器类等;
  • 如矩形,由若干线条组成,线由两点确定;
#include <iostream>
class Point     // holds x,y coordinates
{
	// no constructor, use default
public:
	void SetX(int x) { itsX = x; }
	void SetY(int y) { itsY = y; }
	int GetX()const { return itsX;}
	int GetY()const { return itsY;}
private:
	int itsX;
	int itsY;
};    // end of Point class declaration

class  Rectangle
{
public:
	Rectangle (int top, int left, int bottom, int right);
	~Rectangle () {}

	int GetTop() const { return itsTop; }
	int GetLeft() const { return itsLeft; }
	int GetBottom() const { return itsBottom; }
	int GetRight() const { return itsRight; }

	Point  GetUpperLeft() const { return itsUpperLeft; }
	Point  GetLowerLeft() const { return itsLowerLeft; }
	Point  GetUpperRight() const { return itsUpperRight; }
	Point  GetLowerRight() const { return itsLowerRight; }

	void SetUpperLeft(Point Location)  {itsUpperLeft = Location;}
	void SetLowerLeft(Point Location)  {itsLowerLeft = Location;}
	void SetUpperRight(Point Location)  {itsUpperRight = Location;}
	void SetLowerRight(Point Location)  {itsLowerRight = Location;}

	void SetTop(int top) { itsTop = top; }
	void SetLeft (int left) { itsLeft = left; }
	void SetBottom (int bottom) { itsBottom = bottom; }
	void SetRight (int right) { itsRight = right; }

	int GetArea() const;

private:
	Point  itsUpperLeft;
	Point  itsUpperRight;
	Point  itsLowerLeft;
	Point  itsLowerRight;
	int    itsTop;
	int    itsLeft;
	int    itsBottom;
	int    itsRight;
};
#include "Rectangle.h"
Rectangle::Rectangle(int top, int left, int bottom, int right)
{
	itsTop = top;
	itsLeft = left;
	itsBottom = bottom;
	itsRight = right;

	itsUpperLeft.SetX(left);
	itsUpperLeft.SetY(top);

	itsUpperRight.SetX(right);
	itsUpperRight.SetY(top);

	itsLowerLeft.SetX(left);
	itsLowerLeft.SetY(bottom);

	itsLowerRight.SetX(right);
	itsLowerRight.SetY(bottom);
}

int Rectangle::GetArea() const
{
	int Width = itsRight-itsLeft;
	int Height = itsTop - itsBottom;
	return (Width * Height);
}

int main()
{
	//initialize a local Rectangle variable
	Rectangle MyRectangle (100, 20, 50, 80 );

	int Area = MyRectangle.GetArea();

	std::cout << "Area: " << Area << "\n";
	std::cout << "Upper Left X Coordinate: ";
	std::cout << MyRectangle.GetUpperLeft().GetX();
	return 0;
}

执行结果如下:

Area: 3000
Upper Left X Coordinate: 20请按任意键继续. . .

6.9 struct

C++中结构体和类相似

  • 结构体成员默认为公有成员,类默认为私有;
  • C的结构体没有类方法;
#include <iostream>
struct Point     // holds x,y coordinates
{
	// no constructor, use default
public:
	void SetX(int x) { itsX = x; }
	void SetY(int y) { itsY = y; }
	int GetX()const { return itsX;}
	int GetY()const { return itsY;}
private:
	int itsX;
	int itsY;
};    // end of Point class declaration

struct  Rectangle
{
public:
	Rectangle (int top, int left, int bottom, int right);
	~Rectangle () {}

	int GetTop() const { return itsTop; }
	int GetLeft() const { return itsLeft; }
	int GetBottom() const { return itsBottom; }
	int GetRight() const { return itsRight; }

	Point  GetUpperLeft() const { return itsUpperLeft; }
	Point  GetLowerLeft() const { return itsLowerLeft; }
	Point  GetUpperRight() const { return itsUpperRight; }
	Point  GetLowerRight() const { return itsLowerRight; }

	void SetUpperLeft(Point Location)  {itsUpperLeft = Location;}
	void SetLowerLeft(Point Location)  {itsLowerLeft = Location;}
	void SetUpperRight(Point Location)  {itsUpperRight = Location;}
	void SetLowerRight(Point Location)  {itsLowerRight = Location;}

	void SetTop(int top) { itsTop = top; }
	void SetLeft (int left) { itsLeft = left; }
	void SetBottom (int bottom) { itsBottom = bottom; }
	void SetRight (int right) { itsRight = right; }

	int GetArea() const;

private:
	Point  itsUpperLeft;
	Point  itsUpperRight;
	Point  itsLowerLeft;
	Point  itsLowerRight;
	int    itsTop;
	int    itsLeft;
	int    itsBottom;
	int    itsRight;
};
#include "Rectangle.h"
Rectangle::Rectangle(int top, int left, int bottom, int right)
{
	itsTop = top;
	itsLeft = left;
	itsBottom = bottom;
	itsRight = right;

	itsUpperLeft.SetX(left);
	itsUpperLeft.SetY(top);

	itsUpperRight.SetX(right);
	itsUpperRight.SetY(top);

	itsLowerLeft.SetX(left);
	itsLowerLeft.SetY(bottom);

	itsLowerRight.SetX(right);
	itsLowerRight.SetY(bottom);
}

int Rectangle::GetArea() const
{
	int Width = itsRight-itsLeft;
	int Height = itsTop - itsBottom;
	return (Width * Height);
}

int main()
{
	//initialize a local Rectangle variable
	Rectangle MyRectangle (100, 20, 50, 80 );

	int Area = MyRectangle.GetArea();

	std::cout << "Area: " << Area << "\n";
	std::cout << "Upper Left X Coordinate: ";
	std::cout << MyRectangle.GetUpperLeft().GetX();
	return 0;
}

执行结果如下:

Area: 3000
Upper Left X Coordinate: 20请按任意键继续. . .

发表回复