完整目录、平台简介、安装环境及版本:参考《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请按任意键继续. . .