C++ 教程 | 6. 对象与类
He Junze

对象 构成了面向对象程序的基本计算单位,而对象的特征则由相应的 来描述。

  • 对象是用类来创建的,因此,程序中首先要定义类。

类的定义

在 C++ 中,类是一种用户自定义类型,定义形式如下:class < 类名 > { < 成员描述 > } ;
成员包括:数据成员 成员函数

  • 类成员标识符的作用域为 整个类定义范围
  • 类中前面可以使用后面定义的成员名

1. 数据成员

数据成员是对对象所包含的数据描述,它们可以是常量和变量,类型可以是任意的 C++ 类型(void 除外) 。
C++ 类中数据成员的描述格式与 C 的结构类型成员的描述格式相同,例如:

1
2
3
4
class Date { // 类定义
  ......
int year,month,day;  // 数据成员描述
};

在类中说明一个数据成员的类型时,如果未见到相应类型的定义,或相应的类型未定义完,则该数据成员的类型只能是这些类型的指针或引用类型。例如:

1
2
3
4
5
6
7
8
9
class A;    //A 是在程序其它地方定义的类,这里是声明。
class B {
A a; //Error,未见 A 的定义。
B b; //Error,B 还未定义完,递归了!
A *p;  //OK
B *q;  //OK
A &aa; //OK
B &bb; //OK
};

2. 成员函数

成员函数是对对象包含的数据所能实施的操作描述。

  • 成员函数的实现(函数体)可以放在类定义中,例如:
1
2
3
4
class A
{     ......
void f() {...} // 建议编译器按内联函数处理
};
  • 成员函数的实现也可以放在类定义外,例如:
1
2
3
4
5
class A {
    ......
void f(); // 声明
};
void A::f() { ... } // 要用 ** 类名受限 **

类名受限:在类定义外实现成员函数时,要使用< 类名 >::f()“类名受限”的形式。

在 C++ 中,也允许在结构(struct)和联合(union)中定义函数,但它们的成员访问控制与类不同!为什么?

类成员的重载

成员函数名是可以重载的,它遵循一般函数名的重载规则。例如:

1
2
3
4
5
6
class A
{ .......
void f();
int f(int i);
double f(double d);
};

类成员的访问控制

在 C++ 的类定义中,可以用下面的成员访问修饰符来控制外部对类成员的访问限制:

  • public:访问不受限制

  • private:只能在本类和友元的代码中访问

    • 类定义内部 可以访问 private 成员,在类定义外部不可访问。
  • protected:只能在本类、友元和派生类的代码中访问

1
2
3
4
5
6
7
8
9
10
11
class A
{   public: // 访问不受限制。
int x;
void f();
private: // 只能在本类和友元的代码中访问。
int y;
void g();
protected: // 只能在本类、友元和派生类的代码中访问。
int z;
void h();
};
  • 在 C++ 的类定义中,可以有 多个public、private 和 protected 访问控制说明;

  • 默认 访问控制是 private
    例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A
{        int m,n; //m,n 的访问控制为 private。
   public:
       int x;
       void f()
   private:
       int y;
       void g();
   protected:
       int z;
       void h();
   public:
       void f1();
};

结构(struct)和联合(union)成员的默认访问控制是 public。

对象

对象的创建

1. 直接方式

通过在程序中定义一个类型为类的变量来实现。例如:

1
2
3
4
5
6
7
8
9
10
class A
{   public:
void f();
void g();
private:
int x,y;
}
......
A a1;  // 创建一个 A 类的对象。
A a2[100]; // 创建 100 个 A 类对象。
  • 对象在进入相应变量的生存期时创建,通过变量名来标识和访问。相应变量的生存期结束时,对象消亡。

  • 分为:全局对象、局部对象。

2. 间接方式

在程序运行时刻,用 new 创建对象(称为动态对象),用 delete 撤消它(使之消亡)。动态对象需要通过 指针 来标识和访问。使用 delete 的原因:如果动态对象不再被指针指向,则无法访问。若没有及时删除,则会导致内存泄漏。
1. 单个动态对象的创建与撤消

1
2
3
4
A *p;
p = new A;  // 创建一个 A 类的动态对象。
… *p … // 通过 p 访问动态对象
delete p;   // 撤消 p 所指向的动态对象。

2. 动态对象数组的创建与撤消

1
2
3
4
A *q;
q = new A[100];   // 创建一个动态对象数组。
...q[i]... // 或,*(q+i),访问动态对象数组中的第 i 个对象
delete []q;   // 撤消 q 所指向的动态对象数组。

注意:动态对象一般不采用 C 的库函数 malloc、calloc 来创建和用 free 来撤消!
Java 中不需要 delete,java 编译器会自动检测是否有多余动态对象(没有被指针指向的对象),则会自动删除它来释放空间。

成员对象

对于类的数据成员,其类型可以是另一个类。即,一个对象可以包含另一个对象,后者称为成员对象。例如:

1
2
3
4
5
6
7
8
9
class A
{ ...
};
class B
{ ...
A a; // 成员对象
...
};
B b; // 对象 b 包含一个成员对象:b.a

成员对象跟随包含它的对象一起创建和消亡。

对象的操作

.->来操作对象

同类对象之间可以相互赋值

1
2
Date yesterday,today,some_day;
some_day = yesterday; // 默认是把对象 yesterday 的数据成员分别赋值给对象 some_day 的相应数据成员

取对象地址

1
2
3
Date today;
Date *p_date;
p_date = &today; // 把对象 today 的地址赋值给指针 p_date

引用对象的情况

  1. 把对象作为 参数 传给函数。例如:

1
2
3
4
5
6
7
8
9
void f(Date d) // 创建一个新对象 d,其数据成员用实参对象的数据成员对其初始化
{ ...... 
}
void g(Date &d) // 不创建新对象,d 就是实参对象!
{ ...... 
}
Date today;
f(today); // 调用函数 f,对象 today 不会被 f 修改
g(today); // 调用函数 g,对象 today 会被 g 修改!
  1. 把对象作为 函数的返回值。例如,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Date f(Date &d)
{ d.print(); // 输出:2020.2.20
  return d;  // 创建一个临时对象作为返回值,用 d 对其初始化
}
Date& g(Date &d)
{ d.print(); // 输出:2020.2.20
  return d;  // 不创建新对象,把对象 d 作为返回值
}
Date some_day; // 创建一个日期对象
some_day.set(2020,2,20); 
f(some_day).set(2017,3,13); some_day.print(); //?
g(some_day).set(2017,3,13); some_day.print(); //?
// 前者显示:2020.2.20,因为修改的是临时对象
// 后者显示:2017.3.13,因为修改的是 some_day!
 REWARD AUTHOR
 Comments
Comment plugin failed to load
Loading comment plugin