感谢【北京大学】程序设计实习 (C++版) 郭炜/刘家瑛老师老师的辛勤付出,花了一段时间整理课的内容
目录
第2周 类和对象初步
2-1 面向对象程序设计方法
1结构化程序设计:
• 复杂的大问题 à 层层分解/模块化 à 若干子问题
• 自顶向下, 逐步求精
结构化程序设计的问题:
2 面向对象的程序设计
面向对象的程序设计方法
面向对象的程序 = 类 + 类 + … + 类
设计程序的过程 à 设计类的过程
2-2面向对象程序设计语言发展过程
C++语言的历史:
1979年, AT&T实验室Dr. Bjarne Stroustrup开始发明开始的版本称为: C with Classes
1983年8月, 第一个C++实现投入使用
1983年10月, 正式命名为C++ (CPlusPlus)
1985年10月, Bjarne完成了经典巨著The C++ Programming Language (第一版)
C++标准的发展:
1989年, C++2.0版发布
1994年, ANSI C++标准发布
1998年, ANSI和ISO联合发布 “C++98” 标准加入STL (Standard Template Library) – 泛型设计
2003年, ISO的C++标准委员会发布 “C++03” 标准
2005年, “Library Technical Report 1” (TR1) 技术报告发布
2011年9月, ISO标准委员会通过了新的C++标准 -- C++11
常用的C++编译器:
GCC
Visual C++ 10.0
Dev C++
Eclipse
Borland C++Builder
第一个面向对象语言:Simula
Simula
1967年, Ole-Johan Dahl
和Kristen Nygaard
发布Simula 67
• 提出了类(class)和子类(subclass)的概念
• 程序设计语言发展史上的一个重要的里程碑
Bjarne Stroustrup
• http://www2.research.att.com/~bs/
• AT&T Fellow, AT&T Labs - Research
• Distinguished Professor
• Holder of the College of Engineering Chair
Computer Science at Texas A&M University
• C++ Bible
• The C++ Programming Language (4th Edition)
• Addison-Wesley ISBN 978-0321563842. May 2013.
2-3从客观对象抽象出的例子
程序如:
class CRectangle {
public:
int w, h;
void Init( int w_, int h_ ) {
w = w_; h = h_;
}
int Area() {
return w * h;
}
int Perimeter() {
return 2 * ( w + h );
}
}; //必须有分号
int main() {
I nt w, h;
CRectangle r; //r是一个对象
cin >> w >> h;
r.Init(w, h);
cout << r.Area() << endl << r. Perimeter();
return 0;
}
类定义的变量 à 类的实例 à “对象”
对象的内存分配:
a.对象的内存空间
• 对象的大小 = 所有成员变量的大小之和
• E.g. CRectangle类的对象, sizeof(CRectangle) = 8
b.每个对象各有自己的存储空间
• 一个对象的某个成员变量被改变, 不会影响到其他的对象
对象间的运算:
对象之间可以用 ‘=’ 进行赋值
不能用 ‘==’, ‘!=’, ‘>’, ‘<’, ‘>=’, ‘<=’进行比较
• 除非这些运算符经过了 “重载”
访问类的成员变量和成员函数
用法1: 对象名.成员名
CRectangle r1, r2;
r1.w = 5;
r2.Init(3,4);
用法2: 指针->成员名
CRectangle r1, r2;
CRectangle * p1 = & r1;
CRectangle * p2 = & r2;
p1->w = 5;
p2->Init(3,4); //Init作用在p2指向的对象上
用法3: 引用名.成员名
CRectangle r2;
CRectangle & rr = r2;
rr.w = 5;
rr.Init(3,4); //rr的值变了,r2的值也变
另一种输出结果的方式
void PrintRectangle(CRectangle & r) {
cout << r.Area() << ","<< r.Perimeter();
}
CRectangle r3;
r3.Init(3,4);
PrintRectangle(r3);
类的成员函数的另一种写法
成员函数体和类的定义分开写
class CRectangle
{
public:
int w, h;
int Area(); //成员函数仅在此处声明
int Perimeter() ;
void Init( int w_, int h_ );
};int CRectangle::Area() {
return w * h;
}
int CRectangle::Perimeter() {
return 2 * ( w + h );
}
void CRectangle::Init( int w_, int h_ ) {
w = w_; h = h_;
}
调用通过: 对象 / 对象的指针 / 对象的引用
2-4 类成员的可访问范围
类成员的可访问范围
关键字 -- 类成员可被访问的范围
• private: 指定私有成员, 只能在成员函数内被访问
• public: 指定公有成员, 可以在任何地方被访问
• protected: 指定保护成员
三种关键字出现的次数和先后次序都没有限制
分析:
szName à char szName[5]
如果szName不是私有, 需要修改全部:
strcpy(man1.szName, “Tom1234567889”);
如果将szName变为私有,
所有对 szName的访问都是通过成员函数来进行,
man1.setName( “Tom12345678909887”);
2-5 内联函数和重载函数
函数重载:(参数缺省情况)
需要注意二义性;
当函数名相同和参数列表相同,如果返回值的类型相同,则是函数重定义(错误)
2-6 构造函数
基本概念:
- 成员函数的一种
- 名字与类名相同,可以有参数,不能有返回值(void也不行)
- 作用是对对象进行初始化,如给成员变量赋初值
- 如果定义类时没写构造函数,则编译器生成一个默认的无参数的构造函数
- 默认构造函数无参数,不做任何操作
- 如果定义了构造函数,则编译器不生成默认的无参数的构造函数
- 对象生成时构造函数自动被调用。对象一旦生成,就再也不能在其上执行构造函数
- 一个类可以有多个构造函数
- 为什么需要构造函数:
构造函数执行必要的初始化工作,有了构造函数,就不必专门再写初始化函数,也不用担心忘记调用初始化函数。
有时对象没被初始化就使用,会导致程序出错。
程序示例:
class Complex {
private :
double real, imag;
public:
void Set( double r, double i);
}; //编译器自动生成默认构造函数
Complex c1; //默认构造函数被调用
Complex * pc = new Complex; //默认构造函数被调用
程序示例2:
class Complex {
private :
double real, imag;
public:
Complex( double r, double i = 0);
};
Complex::Complex( double r, double i) {
real = r; imag = i;
}
Complex c1; // error, 缺少构造函数的参数
Complex * pc = new Complex; // error, 没有参数
Complex c1(2); // OK
Complex c1(2,4), c2(3,5);
Complex * pc = new Complex(3,4);
可以有多个构造函数,参数个数或类型不同
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;
}
构造函数在数组中的使用:
class CSample {
int x;
public:
CSample() {
cout << "Constructor 1 Called" << endl;
}
CSample(int n) {
x = n;
cout << "Constructor 2 Called" << endl;
}
};
int main(){
CSample array1[2];
cout << "step1"<<endl;
CSample array2[2] = {4,5};
cout << "step2"<<endl;
CSample array3[2] = {3};
cout << "step3"<<endl;
CSample * array4 =
new CSample[2];
delete []array4;
return 0;
}
输出:
Constructor 1 Called
Constructor 1 Called
step1
Constructor 2 Called
Constructor 2 Called
step2
Constructor 2 Called
Constructor 1 Called
step3
Constructor 1 Called
Constructor 1 Called
class Test {
public:
Test( int n) { }
Test( int n, int m) { }
Test() { }
};
Test array1[3] = { 1, Test(1,2) };
// 三个元素分别用(1),(2),(3)初始化
Test array2[3] = { Test(2,3), Test(1,2) , 1};
// 三个元素分别用(2),(2),(1)初始化
Test * pArray[3] = { new Test(4), new Test(1,2) };
//两个元素分别用(1),(2) 初始化