Classes (1) Lecture 6.

Slides:



Advertisements
Similar presentations
C++语言程序设计教程 第5章 构造数据类型 第6章 C++程序的结构.
Advertisements

6. 6 Overloading methods and constructors 6
Memory Pool ACM Yanqing Peng.
第4章 数组 数组是由一定数目的同类元素顺序排列而成的结构类型数据 一个数组在内存占有一片连续的存储区域 数组名是存储空间的首地址
7.2 访问控制 —— 公有继承 公有继承练习 //Point.h #ifndef _POINT_H #define _POINT_H class Point { //基类Point类的定义 public: //公有函数成员 void initPoint(float x = 0, float.
第14章 c++中的代码重用.
Data Abstraction: The Walls
第八章 类和对象.
第10讲 Java面向对象编程基础(4) 教学目标 主要内容.
Using C++ The Weird Way Something about c++11 & OOP tricks
Chapter 7 Search.
Linked List(串列) Why Linked List? Pointer Dynamic Allocation
走向C++之路 WindyWinter WindyWinter感谢诸位前来捧场。
struct 可以在同一個名稱下擁有多種資料型態。使用struct能讓資料的存取和處理更為靈活。
Chap 18 類別與物件 夫有土者,有大物也。有大物者,不可以物。 物而不物,故能物物。 明乎物物者之非物也,豈獨治天下百姓而已哉!
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
Chap 3 堆疊與佇列 Stack and Queue.
EBNF 请用扩展的 BNF 描述 C语言里语句的结构; 请用扩展的 BNF 描述 C++语言里类声明的结构;
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
Classes Lecturer: 曾學文.
Scope & Lifetime 前言 Local Scope Global Functions & Objects
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
第11章 运算符重载 什么是运算符重载 运算符重载的方法 几个特殊的运算符的重载 自定义类型转换运算符 运算符重载实例.
刘胥影 东南大学计算机学院 面向对象程序设计1 2011~2012第3学期 刘胥影 东南大学计算机学院.
·线性表的定义及ADT ·线性表的顺序存储结构 ·线性表的链接存储结构 · 单向循环链表 · 双链表、双向循环链表 · 一元多项式的加法
Classes: A Deeper Look, Part 1
Classes: A Deeper Look, Part 2
创建型设计模式.
刘胥影 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 东南大学计算机学院.
Object-Oriented Programming:
類別樣板 Class Template 類似函式樣板 由類別樣板產生的類別稱為類別樣版的實體(instance)
授课老师:龚涛 信息科学与技术学院 2018年3月 教材: 《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
程序设计期末复习 黎金宁
第三章 C++中的C 面向对象程序设计(C++).
授课老师:龚涛 信息科学与技术学院 2018年4月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
辅导课程六.
第九單元 Classes and data abstraction I
第六章 数据抽象-类 胡昊 南京大学计算机系软件所.
类类型 C++支持的内置类型和操作,如 int i=10; i=i%6; i=i+4;
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
第七章 操作符重载 胡昊 南京大学计算机系软件所.
Classes (2) Lecture 7.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
$9 泛型基础.
Speaker: Liu Yu-Jiun Date: 2009/4/29
C#面向对象程序设计 $6 深入理解类.
C#程序设计基础 $3 成员、变量和常量.
OOP Recitation Course Speaker: Liu Yu-Jiun Date: 2009/3/25.
Oop8 function函式.
Speaker: Liu Yu-Jiun Date: 2009/5/6
Inheritance -II.
第三章 数据抽象.
本节内容 类成员的访问控制 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第九章 物件導向-進階.
第 9 章 建構函式與解構函式.
辅导课程十五.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第 8 章 标准模板库STL 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
#include <iostream.h>
第7章 模板 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
本节内容 动态链接库 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
JAVA 程式設計與資料結構 第三章 物件的設計.
對於成員(member)存取權的限制 成員的資料被毫無限制的存取,任誰都可以指定任意值給成員,Java語言為了防止這種現象的產生,規定:有一種成員的資料不能任由類別外部的任何人隨意存取。
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
資料結構與C++程式設計進階 C++與資料結構 講師:林業峻 CSIE, NTU 7/ 5, 2010.
Presentation transcript:

Classes (1) Lecture 6

Abstract data types (ADTs) class Sales_item { public: Sales_item(const string &book); double avg_price( ) const; Sales_item& operator+= (const Sales_item &item); string isbn; unsigned int units_sold; double revenue; }; A class of objects whose logical behavior is defined by a set of values and a set of operations.

Abstract data types (ADTs) class Sales_item { public: Sales_item(const string &book); double avg_price( ) const; Sales_item& operator+= (const Sales_item &item); string isbn; unsigned int units_sold; double revenue; }; ADT not ADT struct Sales_data { string isbn; unsigned int units_sold; double revenue; };

Declaring C++ classes 类名 class或struct关键字 成员函数访问权限 成员函数声明 成员变量访问权限 class Sales_item { public: Sales_item(const string &book); double avg_price( ) const; Sales_item& operator+= (const Sales_item &item); string isbn; unsigned int units_sold; double revenue; }; 成员函数访问权限 成员函数声明 成员变量访问权限 成员变量声明 结尾分号

Member functions Definition double avg_price( ) const { return units_sold ? revenue / units_sold : 0; } Call Sales_item cpp_primer(“978-0-321-71411-4”); cout << cpp_primer.avg_price( );

Member functions 从调用语句cpp_primer.avg_price( )编程者知道是cpp_primer这一Sales_item类的对象调用了Sales_item类的成员函数avg_price( ),但在运行时,机器如何得知caller的信息? double avg_price( ) const { return units_sold ? revenue / units_sold : 0; } double Sales_item::avg_price(const Sales_item* const p) { return p->units_sold ? p->revenue / p->units_sold : 0; }

this pointer Member functions access the object on which they were called through an extra, implicit parameter named this. this is initialized with the address of the object on which the function was invoked. double avg_price( ) const { return units_sold ? revenue / units_sold : 0; } double avg_price( ) const { return this->units_sold ? this->revenue / this->units_sold : 0; }

const member functions this指针的类型是Sales_item* const,因为其指向的对象是当前调用函数的对象,该指向不可更改。 类成员函数参数列表后跟随的关键字const是进一步限定this指针的类型为const Sales_item* const,限定该成员函数不可更改任何成员变量。 double avg_price( ) const { return units_sold ? revenue / units_sold : 0; } double avg_price( ) const { return this->units_sold ? this->revenue / this->units_sold : 0; }

Separate compilation // 调用avg_price( )… class scope Sales_item.h #ifndef SALES_ITEM_H_ #define SALES_ITEM_H_ class Sales_item { public: double avg_price( ) const; // other code pieces }; #endif Sales_item.h #include “Sales_item.h” double Sales_item::avg_price( ) const { return units_sold ? revenue/units_sold : 0; } Sales_item.cpp // 调用avg_price( )… Main.cpp

Returning the caller object 实现一个成员函数,将一条Sales_item记录与当前记录合并:revenue和units_sold分别对应相加。 ??? combine(const Sales_item &item) { this->revenue += item.revenue; this->units_sold += item.units_sold; return ???; }

Returning the caller object void combine(const Sales_item &item) { this->revenue += item.revenue; this->units_sold += item.units_sold; } Which one is better? Sales_item& combine(const Sales_item &item) { this->revenue += item.revenue; this->units_sold += item.units_sold; return *this; } Version 1 Version 2

Returning the caller object 现有4个Sales_item,需要把它们合并起来。 Version 1 item1.combine(item2); item1.combine(item3); item1.combine(item4); combine的返回值是item1的引用 Version 2 item1.combine(item2).combine(item3).combine(item4);

Constructors 构造函数 构造函数用于定义如何创建/初始化一个对象。 构造函数的函数名与类名相同 构造函数没有返回值 一个类可以有多个重载的构造函数 构造函数不可以被声明为const 当且仅当程序员没有声明任何构造函数时,编译器会自动生成一个默认构造函数

Constructors 默认构造函数,各built-in成员变量采用默认的初始值 默认构造函数,显式指定初始值 构造函数初始化列表 Sales_item::Sales_item( ) { } Sales_item::Sales_item( ) = default; 默认构造函数,显式指定初始值 Sales_item::Sales_item( ): isbn(“”), revenue(0.0), units_sold(0) { } 构造函数初始化列表 Sales_item::Sales_item(const string &book): isbn(book), revenue(0.0), units_sold(0) { } Sales_item::Sales_item(const string &book, double r, unsigned int u): isbn(book), revenue(r), units_sold(u) { } 重载构造函数

Constructors 每个类只能有一个默认构造函数。 Why? 如果定义了非默认构造函数,那么编译器不会自动生成默认构造函数。 class Sales_item { public: Sales_item(const string &book); // other code pieces }; Sales_item cpp_primer; error: no matching constructor for initialization of 'Sales_item'

Constructor initializer list 必须使用构造函数初始化列表的情况:初始化常量成员 class SomeClass { public: SomeClass( ): N(0) { } private: const int N; }; class SomeClass { public: SomeClass( ) { N = 0; } private: const int N; }; ok error: cannot assign to non-static data member 'N' with const-qualified type 'const int'

Constructor initializer list 必须使用构造函数初始化列表的情况:初始化引用成员 class ClassA { public: ClassA(ClassB &v): obj(v) { } private: ClassB &obj; }; class ClassA { public: ClassA(ClassB &v) { obj = v; } private: ClassB &obj; }; ok error: constructor for 'ClassA' must explicitly initialize the reference member ‘obj'

Constructor initializer list 成员变量按照在类中声明的顺序被初始化。 好的编程习惯是按在类中声明的顺序写constructor initializer list。 class SomeClass { public: SomeClass( ): j(1), i(j) { } private: int i; int j; }; warning: field ‘j' will be initialized after field 'i'

Copy constructor 每个类可以有一个拷贝构造函数,用来定义类的对象如何被复制。 当一个类没有拷贝构造函数时,编译器自动生成一个拷贝构造函数。 Sales_item(const Sales_item &item): isbn(item.isbn), revenue(item.revenue), units_sold(item.units_sold) { }

Deep copy vs. shallow copy 编译器自动生成的拷贝构造函数只能实现浅拷贝。 class ClassA { public: ClassA(ClassB *p): ptr(p) { } private: ClassB *ptr; }; ptr ptr shallow copy

Deep copy vs. shallow copy class ClassA { public: ClassA(ClassB *p): ptr(p) { } ClassA(const ClassA &obj) { ptr = new ClassB(*obj.ptr); } private: ClassB *ptr; }; ptr ptr deep copy

Constructors 建议:不要依赖编译器自动生成的默认/拷贝构造函数。

Destructor 析构函数 当对象结束生命期时由系统自动调用,清理并释放对象所占用的内存。 析构函数的函数名为类名前加上~符号 析构函数没有返回值 一个类只能有1个析构函数 析构函数不可以被声明为const 当程序员没有声明析构函数时,编译器会自动生成一个默认析构函数 建议不要依赖编译器自动生成的析构函数

Destructor 析构函数 多数情况下这段代码可以正常执行、正常结束,但这段代码会造成内存泄漏(memory leak)。 class SomeClass { public: SomeClass(int m): n(m) { ptr = new int[m]; } private: int n; int *arr; }; 多数情况下这段代码可以正常执行、正常结束,但这段代码会造成内存泄漏(memory leak)。

Memory segments The memory is typically divided into a few different segments: Code segment: compiled program BSS segment: zero-initialized global & static variables Data segment: initialized global & static variables Heap: dynamically allocated variables Call stack: function parameters, local variables & other function-related information https://www.learncpp.com/cpp-tutorial/79-the-stack-and-the-heap/ [Nov 16, 2018] Image from https://en.wikipedia.org/wiki/Data_segment [Nov 16, 2018]

Dynamically allocating memory int a = 3; int *p = new int(3); 局部变量,在stack中 动态分配的变量,在heap中 到C++11标准为止,局部数组都要求在编译时确定数组大小,因此在声明/定义时其长度不可以是变量。 GCC提供编译器扩展,接受左侧的写法 size_t n = 7; int b[n]; int *q = new int[n]; 动态分配的数组,在heap中

Advice 学习时要注意区分哪些feature是C++ standard,哪些feature是compiler extension。

Dynamically allocating memory size_t n = 7; int *q = new int[n]; delete [ ] q; int *p = new int(3); delete p; 释放内存 如果程序员不释放,那么在程序结束后由操作系统回收,但程序在正常结束前可能已崩溃。 Heap的使用原则: 需要的时候申请分配,不需要的时候及时释放 通常,谁申请谁释放

Dynamically allocating memory 应判断内存分配是否成功 size_t n = 7; int *q = nullptr; if ( (q = new int[n]) == nullptr) { // 分配不成功,进行异常处理 } // 分配成功,执行后续程序 delete [ ] q; int *p = nullptr; if ( (p = new int(3)) == nullptr ) { // 分配不成功,进行异常处理 } // 分配成功,执行后续程序 delete p;

Destructor 析构函数 析构函数 class SomeClass { public: SomeClass(int m): n(m), ptr(nullptr) { if ( (ptr = new int[m]) == nullptr ) { // 分配失败,执行异常处理 } } ~SomeClass( ) { delete [ ] ptr; } private: int n; int *arr; }; 析构函数

Access control & encapsulation 封装 class Sales_item { public: Sales_item(const string &book); double avg_price( ) const; Sales_item& operator+= (const Sales_item &item); private: string isbn; unsigned int units_sold; double revenue; }; 可以被程序中的其他部分访问 只能被Sales_item的成员访问

Friends 某些情况下,类外部的函数需要访问类的私有数据成员。 换句话说,有些需要访问类的私有数据成员的函数不适合作为类的成员函数。 例:为Sales_item类定义重载的<<操作符,使得可以写cout << obj。 注意:操作符<<必须有两个参数ostream&和const Sales_item&,必须返回ostream&。

Friends 作为public成员函数 class Sales_item { public: ostream& operator<< (ostream &out, const Sales_item &item) { out << item.isbn << “, “ << item.revenue << “, “ << item.units_sold; return out; } }; error: overloaded 'operator<<' must be a binary operator (has 3 parameters)

Friends 作为类外部的函数 error: 'isbn' is a private member of 'Sales_item’ error: 'revenue' is a private member of 'Sales_item' error: 'units_sold' is a private member of 'Sales_item' class Sales_item { // blablabla }; ostream& operator<< (ostream &out, const Sales_item &item) { out << item.isbn << “, “ << item.revenue << “, “ << item.units_sold; return out; }

Friends 不是类的成员函数 参数中没有隐藏的this指针 可以访问类的私有成员 需要在类的内部声明 作为类的友元函数 可以在类的外部定义 作为类的友元函数 class Sales_item { public: friend ostream& operator<< (ostream &out, const Sales_item &item) { out << item.isbn << “, “ << item.revenue << “, “ << item.units_sold; return out; } };

Static members Classes sometimes need members that are associated with the class, rather than with individual objects of the class type. 例:假如有规定每本书的价格不得超过一个上限max_price class Sales_item { public: double max_price; // other code pieces }; 这样行不行?如果上限要更改,怎么办?

Static members max_price不是任何对象的一部分,所有的对象共享一个max_price。 class Sales_item { public: static double max_price; // other code pieces }; max_price不是任何对象的一部分,所有的对象共享一个max_price。

Static members error: undefined symbol ‘Sales_item::max_price’ class Sales_item { public: Sales_item( ) { max_price = 5.0; } static double max_price; // other code pieces }; error: undefined symbol ‘Sales_item::max_price’ max_price不是任何对象的一部分,所以不能通过构造函数初始化。

Static members class Sales_item { public: Sales_item( ) { } static double max_price = 5.0; // other code pieces }; error: non-const static data member must be initialized out of line 非常量的static成员必须在类外定义。

Static members class Sales_item { public: Sales_item( ) { } static double max_price; // other code pieces }; double Sales_item::max_price = 5.0; class Sales_item { public: Sales_item( ) { } static const double MAX_PRICE = 5.0; // other code pieces };

Using static members 不需要Sales_item类的对象 class Sales_item { public: static double max_price; // other code pieces }; double Sales_item::max_price = 5.0; Sales_item::max_price = 6.0; cout << Sales_item::max_price; 不需要Sales_item类的对象

Static member functions 静态成员函数可以使用静态成员变量/函数 静态成员函数不可以使用非静态成员变量/函数 class Sales_item { public: static void updateMaxPrice(double mp) { max_price = mp; } double avg_price( ) const { //… } bool over_priced( ) const { return avg_price( ) > max_price; } static double max_price; // other code pieces }; double Sales_item::max_price = 5.0; 非静态成员函数可以使用静态成员变量/函数

Using static member functions Sales_item item1(“001”, 100.0, 10); item2(“002”, 16.0, 8); cout << item1.over_priced( ) << “, “ << item2.over_priced( ) << endl; Sales_item::updateMaxPrice(12.0); 1, 0 0, 0

Next lecture C++ Primer, Chapters 13~15