面向对象程序设计 OOP

class 类

C实现

typedef struct point {  float x;
	float y;
}	Point;
void print(const Point* p){
	printf(%d %d\n”,p->x,p->y);
}
void move(Point* p,int dx, int dy){
	p->x += dx;  
	p->y += dy;
}
Point a;  
Point b;
a.x =	b.x =	1; 
a.y =	b.y =	1;  
move(&a,2,2);
print(&a);  
print(&b);

C++封装

class Point {  
public:
	void init(int x, int y);
    void move(int dx, int dy);
    void print() const;
private: //可以保护坐标
	int x;
	int y;
};
// 函数返回 类名::函数(){} <Class Name>::<function name>
// ::<function name> 不属于任何一个类,全局函数, ::a全局变量
/*
void S::f() {
	::f(); // Would be recursive otherwise!
	::a++; // Select the global a  
	a--; // The a at class scope
}
*/
void Point::init(int  ix, int iy){
	x =	ix; 
	y =	iy;
}
void Point::move(int  dx,int dy){
	x+= dx; 
	y+= dy;
}
void Point::print() const { // const 代表不能改成员变量,只能访问
	cout << x << ' ' << y << endl;
}
Point a;  a.init(1,2);
a.move(2,2);
a.print();

Objects = Attributes + Services
将数据和操作封装在一起,要求只能用操作与对象交流,不能直接访问到数据本身
用操作访问对象,不能直接访问到数据本身

实现 stash container

container的要求

  • 可以装其他对象的对象。
  • 需要满足基本操作:put(), get() 。
  • 可以动态增长。
    stash :: add()
  • 无类型要求
  • 只能存储同样类型的对象
  • add() , fetch()
  • Expanded when needed
    struct 默认 public , class 默认 private 。
struct Stash {
	int size; // Size of each space
	int quantity; // Number of storage spaces  
	int next; // Next empty space
	// Dynamically allocated array  
	unsigned char* storage;
	// Functions!
	void initialize(int size);  
	void cleanup();
	int add(const void* element);  
	void* fetch(int index);
	int count();
	void inflate(int increase);
};
class Stash {
	int size;     // Size of each space
	int quantity; // Number of storage spaces  
	int next; // Next empty space
	// Dynamically allocated array  
	unsigned char* storage;
	void inflate(int increase);  // 当大小不够时增加,实现动态增长的核心函数
public:
	Stash(int size);
	~Stash(); // 对象消亡时的动作
	int add(const void* element); // 加对象,只能加到最后
	void* fetch(int index); // 根据坐标查看对象
	int count(); // 对象数量
};

设计代码:
声明放在 .h 文件里,具体实现放在同名的 .cpp 文件(#include “.h”)里。

  • Class declaration and prototypes in that class are in the header file (.h).
  • All the bodies of these functions are in the source file (.cpp).

this : 隐藏指针,指向 调用的对象
对象存储时只存成员变量,不存函数,函数公用放在编译器代码段里,调用函数时需要 this 指针把该对象的变量带入函数。

买票机器: Ticket Machine

  • 输入:用户给的钱
  • 输出:余票数量和找的钱
    买票机器
    Ticket Machine
    Turn it into code
    Turn it into code
    class 类 是一种概念,
    object 对象 是类的具体实现,实例。

OOP 的特点:

  • 所有都是对象.
  • A program is a bunch of objects telling each other what to do by sending messages. 调用函数实现用户需求
  • Each object has its own memory made up of other objects. 每个对象有自己的内存
  • Every object has a type. 每个对象有自己的类型 class
  • All objects of a particular type can receive the same messages.

契约精神:
The header is a contract between you and the user of your code.
Structure of C++ program
.cpp 一个编译的单元
.h variables,function prototypes class/struct ,declaration
#include ""会先在本地目录下搜索,<>直接在缺省目录下搜索

#ifndef HEADER_FLAG
#define HEADER_FLAG
// Type declaration here...
#endif // HEADER_FLAG

相当于

#pragma once
// Type declaration here...

对复杂的项目,Makefile多文件编译
example:
包含三个文件的项目:main.c, sum.c, sum.h 并且 sum.h included in both .c files
example of Makefile

sum : main.o	sum.o
	gcc –o sum main.o sum.o

main.o : main.c sum.h
	gcc –c main.c

sum.o : sum.c sum.h
	gcc –c sum.c

use built-in macros to get another (shorter) equivalent makefile

sum : main.o sum.o
	gcc –o	$@	main.o	sum.o // $@ 引用cmd的名字“sum”
main.o sum.o : sum.h  
	gcc –c $*.c // $* 历遍引用cmd中的名字“main.o” “sum.o”

对象的行为

构造和析构函数

构造函数
The name of the constructor is the same as the name of the class.

class Point {
public:
	Point(); // 缺省构造函数,没有任何参数;可以有参数
	~Point(); // 析构函数,对象生命周期结束时自动调用
	void init(int x, int y);
	void print() const;
	void move(int dx, int dy);
private:
	int x;
	int y;
};

Point a;
a.init(1,2);
a.move(2,2);
a.print();

“auto” default constructor
如果你定义了一个构造函数,编译器会保证这个函数正确使用,且一定被使用(写case时需要小心);如果没有定义构造函数,编译器会帮你构造一个缺省构造函数。
析构也有类似的“auto”。
设计时钟: Clock display

  • 输入:–
  • 输出:时钟随时间变化
    抽象 Abstraction:
    将小时和分钟都抽象为进位的类
    模块化 Modularization:
    小时和分钟两个对象模块之间的交互
    Object & Classes
    Object & Classes
    Class Diagram
    Class	Diagram
    Initialization Member Init
  • Directly initialize a member
class Point {  
private:
	const float x, y;
	Point(float xa = 0.0, float ya = 0.0){
		y = ya;
		x = xa;
	};
};
  • Initializer list
class Point {  
private:
	const float x, y;
	Point(float xa = 0.0, float ya = 0.0)
	: y(ya), x(xa){}
};

给了成员变量调用构造函数的机会,而不是赋值,成员在对象构造之前初始化。
需要注意列表里的顺序,以防有先后依赖关系。

拷贝构造函数

void f() {
	Stash students();}

可以是一个函数声明,也可以是调用Stash类的缺省构造函数。
认为是函数声明Stash students(),调用Stash类的缺省构造函数Stash students

// Currency as pass-by-value argument 
void func(Currency p) {  
cout << "X = " << p.dollars(); 
} 
... 
Currency bucks(100, 0); 
func(bucks); // bucks is copied into p

bucks 被复制到 p 中。

在定义时刻,把一个对象的值拷贝到另一个对象中使用 copy constructor

// copy constructor
T::T(const T&);

拷贝构造函数会自动生成,如果你没有编写相应的程序。

若 class 中存在指针,

class Person { 
public: 
   Person(const char *s); 
   ~Person(); 
   void print(); 
   // ... accessor functions 
private: 
   char *name;   // char * instead of string 
   //... more info e.g. age, address, phone 
};

创建拷贝构造函数时的两种写法
两种写法

重载 overloading

同名的函数可以被反复创造

C语言中不允许函数重名。

void print(char * str, int width); // #1
void print(double d, int width); // #2
void print(long l, int width); // #3
void print(int i, int width); // #4
void print(char *str); // #5

print("Pancakes", 15);
print("Syrup");
print(1999.0, 10);
print(1999, 12);
print(1999L, 15);

auto-cast
根据类型转换的规则自动调用函数

void f(short i);  
void f(double d);

f('a'); // f(short i);  
f(2); // f(short i);
f(2L); 
f(3.2); // f(double d);

Default arguments

Stash(int	size, int	initQuantity = 0);

Stash s(20); // size = 20, initQuantity = 0.

defaults must be added from right to left.

int harpo(int n, int m = 4, int j = 5);
int chico(int n, int m = 6, int j);  //illeagle
int groucho(int k = 1, int m = 2, int n = 3);
beeps = harpo(2);
beeps = harpo(1,8);
beeps = harpo(8,7,6);

const object

Constant in object

不能修改 const object 中成员函数的值

int Date::set_day(int d){
	//...error check d here...
	day = d; // ok, non-const so can modify
}

int Date::get_day() const {
	day++; //ERROR modifies data member
	set_day(12); // ERROR calls non-const member
	return day; // ok
}

在头文件和 .cpp 文件中都要写 const

Const and non-const objects:

// non-const object
Date when(1,1,2001); // not a const
int day = when.get_day(); // OK
when.set_day(13); // OK

// const object
const Date birthday(12,25,1994); // const
int day = birthday.get_day(); // OK
birthday.set_day(14); // ERROR

Constant in class

在类中定义常量的变量,只允许初始化

class A {
	const int i;
};

has to be initialized in initializer list of the constructor
编译器会避免为const分配内存

class HasArray {
	const int size;
	int array[size]; // ERROR! 编译时还不能确定 size 的值。
	...
};

Make the const value static:

static const  int size = 100;
class HasArray {
	int array[size]; // OK!
	...
};

Or use “anonymous enum” hack:

Class HasArray{
    enum { size = 100 };
    int array[size];  // OK!}

对象变量

对象作为参数或返回值

// 作为参数
void f(Student i); // 会创建临时的Student 对象
void f(Student *p); // 最好加上 const 如果不想对原本的对象进行修改
void f(Student& i); // 最好加上 const 如果不想对原本的对象进行修改
// 作为返回值
Student f(); // 会创建临时的Student 对象
Student* f(); // 确保指向确切的地址
Student& f(); // 确保引用确切的地址

希望不同模块之间松耦合(简耦合),只依靠函数交流。
紧耦合的例子:

char *foo()
{
	char *p;
	p = new char[10];  
	strcpy(p, "something");  
	return p;
}
void bar()
{
	char *p	= foo();  
	printf("%s", p);  
	delete p;
}

且责任不对等:不满足谁分配谁回收的责任模式。
修改后:

char *foo()
{
	char *p;
	strcpy(p, "something");  
	return p;
}
void bar()
{
	char *p	= foo(p);  
	p = new char[10]; 
	printf("%s", p);  
	delete p;
}

static

可以被private隐藏
与具体对象无关,可以直接用类访问
初始化一次,初始化时不需要写“static”

使用静态成员:

<class name>::<static member>
<object variable>.<static member>

静态成员变量

第一次执行函数时,静态变量会被初始化(只有一次)

void f() {
	static int num_calls = 0; 
	... 
	num_calls++; 
}

第一次调用时被初始化,以后每次调用都会加一。

静态对象变量

class X { 
    X(int, int); 
    ~X(); 
    ... 
};
void f() { 
    static X my_X(10, 20); 
    ... 
}

构造函数在第一次执行时调用,且只进行一次。
析构函数在退出程序时调用,最后构造的静态对象,最先析构。

void f(int x) { 
    if (x > 10) { 
        static X my_X(x, x * 21); 
        ... 
	}
}

跳过静态对象的构造。
与对象构造函数不能跳过不同。

全局对象全局变量

#include "X.h" 
X global_x(12, 34); 
X global_x2(8, 16);

main() is no longer the first function called

一个文件中静态变量的初始化按顺序进行,多文件时需要梳理文件之间的依赖关系。

  • 尽量避免静态变量初始化之间的依赖。
  • 一定要有依赖,最好把有依赖的静态变量按顺序放在一个文件里。

静态成员函数

只能访问静态成员变量。
也只有一次初始化的时机。

没有“this”指针,因为不属于任何对象,属于整个类。
只能访问静态成员变量。
不能被重写覆盖。

名字

用**“::”控制命名冲突的使用,“{}”**控制使用范围

class Marbles { 
	enum Colors { Blue, Red, Green }; 
	... 
}; 

class Candy { 
	enum Colors { Blue, Red, Green }; 
	... 
};
Marbles::Colors;
Candy::Colors;

namespace

// old1.h  
   void f();
   void g();

// old2.h  
   void f();
   void g();
// old1.h
namespace old1 {  
   void f();
   void g();
}
// old2.h
Namespace old2 {
   void f();
   void g();
}
old1::f();
old2::f();

当命名空间名字一致时,编译器会合并同名的命名空间。

using

// Mylib.h
namespace MyLib {
    void foo();
    class Cat {
    public:
        void Meow();
    };
}
// MyLib.cpp
#include ”MyLib.h”

void MyLib::foo() {	cout << "foo\n"; }
void MyLib::Cat::Meow() {	
	cout << "meow\n"; 
}
//-----------------------------------------------------------------
// 节省工作量
void main() { 
    using MyLib::foo; 
    using MyLib::Cat; 
    foo(); 
    Cat c; 
    c.Meow(); 
}
// -----------------------------------------------------------
void main() { 
   using namespace std; 
   using namespace MyLib; 
   foo(); 
   Cat c; 
   c.Meow(); 
   cout << “hello” << endl; 
}

"using"有时会造成错误

// Mylib.h 
namespace XLib { 
    void x(); 
    void y(); 
} 

namespace YLib { 
    void y(); 
    void z(); 
}

void main() { 
   using namespace XLib; 
   using namespace YLib; 
   x(); // OK 
   y(); // Error: ambiguous 
   XLib::y(); // OK, resolves to XLib 
   z(); // OK 
}

面对合并命名空间时的冲突,使用“using”指定

namespace first { 
  void x(); 
  void y(); 
} 
namespace second { 
  void y(); 
  void z(); 
}

namespace mine { 
    using namespace first; 
    using namespace second; 
    using first::y(); // resolve clashes to first::x() 
    void mystuff(); 
    ... 
}

using function
The derived class is able to “using” functions of its parent class
继承

Inline

内联函数:在调用时展开,嵌入到代码中,避免调用开销,和宏类似。

int f(int i) {
	return i*2;
}
main() {
	int a = 4;
	int b = f(a);
}

写成内联函数

inline int f(int i) {
	return i * 2;
}
main() {
	int a = 4;
	int b = f(a);
}

等价于

main() {
	int a = 4;
	int b = a + a;
}
  • 内联函数的实现必须在头文件里
  • 内联是声明不是定义

坏处是会导致代码变大。不能滥用

宏和内联函数

#define f(a) (a)+(a)
main(){
  double a = 4;

  printf(%d”, f(a));
}

宏是字符替换

inline int f(int i){
  return i*2;
}

main(){
  double a = 4;

  printf(%d”, f(a));
}

内联函数是嵌入

Inline may not in-line
函数过长参数过多或递归形式,编译器会忽略内联的关键字。

定义在类内的成员函数自动是内联函数。

class	Cup {	
	int color;
public:
	int getColor() { return color; }
	void setColor(int color){
		this->color = color;
	} // 也可以写在函数外面,让接口看起来更加干净
};

Code-reuse

Composition

对象的组合

  • Objects can be used to build up other objects
  • Inclusion by reference allows sharing

It is the relationship of “has-a”
在这里插入图片描述

Inheritance

继承
回想钟表实现的例子,将公有特征抽象出来变成父类,子类继承后,不需要重复父类已经实现的功能。

分享共用

  • Member data
  • Member functions
  • Interfaces
    继承

子类是父类的扩充
子类是父类的扩充
例子DoME
存储用户输入的CDs和DVDs的信息,一个简单的信息管理系统,允许简单的检索。
CD informationDVD information
DoME classes
使用组合
Class diagram
Object Model

source code
可扩展性差,需要改动的位置多,最小化改动。
使用继承
Solution - inheritance
Class	 diagram
Using inheritance

Inheritance	hierarchies
Inheritance
It is the relationship of “is-a”

Public:使用继承时可见
Protect:继承时可见
Private:都不可见

class Employee {
public:
	Employee( const std::string& name,const std::string& ssn );
	const std::string& get_name() const;
	void print(std::ostream& out) const;
	void print(std::ostream& out, const std::string& msg) const;
protected:
	std::string m_name;
	std::string m_ssn;
};

Constructor for Employee

Employee::Employee( const string& name,
		const string& ssn )
: m_name(name), m_ssn( ssn)
{
	// initializer list sets up the values!
}

Employee member functions

inline const std::string& Employee::get_name() const
{
	return m_name;
}
inline void Employee::print( std::ostream& out ) const 
{
	out << m_name << endl;
	out << m_ssn << endl;
}
inline void Employee::print(std::ostream& out, const std::string& msg) const 
{
	out << msg << endl;
	print(out);
}

Now add Manager

class Manager : public Employee {
public:
    Manager(const std::string& name, 
         const std::string& ssn, 
         const std::string& title);
    const std::string title_name() const;
    const std::string& get_title() const;
    void print(std::ostream& out) const;
private:
    std::string m_title;
};

Base class is mentioned by class name

Manager::Manager( const string& name, const string&
	ssn, const string& title = "" )
	:Employee(name, ssn), m_title( title )
{
}

Manager member functions

inline void Manager::print( std::ostream& out ) const 
{
	Employee::print( out ); //call the base class print
	out << m_title << endl;
}

inline const std::string& Manager::get_title() const
{
	return m_title;
}

inline const std::string Manager::title_name() const
{
	return string( m_title + ": " + m_name ); 
	// access base m_name
}

Uses

int main () {
	Employee bob( "Bob Jones", "555-44-0000" );
	Manager bill( "Bill Smith", "666-55-1234", 				"ImportantPerson" );

	string name = bill.get_name(); // okay Manager 	inherits Employee
	//string title = bob.get_title(); // Error -- bob is an Employee!
	cout << bill.title_name() << '\n' << endl;
	bill.print(cout);
	bob.print(cout);
	bob.print(cout, "Employee:");
	//bill.print(cout, "Employee:"); // Error hidden!
}

Polymorphism

向上转换,动态绑定
使用“virtual”触发

class XYPos {...};    // x.y point
class Shape{
public:
	Shape();
	virtual ~Shape();
	virtual void render();
	void move(const XYPos&);
	virtual void resize();
protected:
	XYPos center;
}
class Ellipse: public Shape{
public:
	Ellipse(float maj, foat minr);
	virtual void render();  // will define own
protected:
	float major_axis, minor_axis;
};

class Circle: public Ellipse{
public:
	Circle(float radius) : Ellipse(radius, radius) {}
	virtual void render();
};
void render(Shape* p){
	p->render();  	// calls correct render function
}					// for given Shape!
void func(){
	Ellipse ell(10, 20);
	ell.render();
	Circle circ(40);
	circ.render();
	render(&ell);
	render(&circ);

}

直接赋值

Ellipse elly(20F, 40F);
Circle circ(60F);
elly =	circ;

父类 = 子类;\ 会把父类中不存在而子类存在的砍掉

指针

Ellipse* elly = new Ellipse(20F, 40F);
Circle* circ = new Cricle(60F);
elly = circ;
elly->render();// Circle::render()

能够调用到子类的

引用

void func(Ellipse& elly) {  
	elly.render();
}

Circle	circ(60F);  
func(circ);

能够调用到子类的

Virtual destructors 虚拟析构函数
根据虚拟表寻找合适的析构函数进行调用

Overriding 虚函数重载

class Base {
public:
	virtual void func();
}
class Derived :	public Base {
public:
	virtual void func();
	//overrides Base::func()
}
void  Derived::func() {
	cout << "In Derived::func!";
	Base::func(); // call to base class
}

允许子类重载函数返回的值可以是父类返回值的子类。

class Expr{
public:
	virtual Expr* newExpr();
	virtual Expr& clone();
	virtual Expr self();
};

class BinaryExpr: public Expr{
public:
	virtual BinaryExpr* newExpr();	// ok
	virtual BinaryExpr& clone();	// ok
	virtual BinaryExpr self();  	// Error!
};

同一个虚函数可以有不同的参数列表

class Base {  public:
	vritual void func();
	vritual void func(int);
};

纯虚

class CDevice { 
public: 
	virtual ~CDevice(); 
	virtual int read(...)   = 0; 
	virtual int write(...)  = 0; 
	virtual int open(...)   = 0; 
	virtual int close(...)  = 0; 
	virtual int ioctl(...)  = 0; 
};

不能被实例化,不能用来定义对象。
定义了接口规范

STL 标准模板库

container 容器对象 collection 集合对象 的实现

实现了很多的数据结构和算法
可以提供很多存储对象的数据结构
containers

  • Vector (expandable array)
  • Deque (expandable array, expands at both
    ends)
  • List (double-linked)
  • Sets and Maps

顺序容器
vector: variable array
deque: dual-end queue
list: double-linked-list
forward_list: as it
array: as “array”
string: char. array

vector

#include <iostream>
#include <vector>  
using namespace std;
int main( ) {
	vector<int> x;
	for (int a=0; a<1000; a++) 
		x.push_back(a);
	vector<int>::iterator p;
	for (p=x.begin(); p<x.end(); p++)
		cout << *p << " ";  
	return 0;
}

generic classes 泛型类

Basic Vector Operations

list

#include <iostream>  
using namespace std;
#include <list>
#include <string>
int main( ) {  
	list<string> s;
	s.push_back("hello");  	
	s.push_back("world");  	
	s.push_front("tide");  	
	s.push_front("crimson");  	
	s.push_front("alabama");  	
	list<string>::iterator p;
	for (p=s.begin(); p!=s.end(); p++) // 注意结束循环条件 	
		cout << *p << " ";
	cout << endl;
}

map

存储一对变量,关键字和数据

#include <map>
#include <string>  
using namespacce std;
int main(){
	map<string, float> price;  
	price["snapple"] = 0.75;
	price["coke"] = 0.50;  
	string item;
	double total=0;  
	while ( cin >> item )
		total += price[item];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值