Presentation is loading. Please wait.

Presentation is loading. Please wait.

(欢迎baj队长携夫人莅临指导,)WindyWinter感谢诸位前来捧场。 WindyWinter

Similar presentations


Presentation on theme: "(欢迎baj队长携夫人莅临指导,)WindyWinter感谢诸位前来捧场。 WindyWinter "— Presentation transcript:

1 走向C++之路 WindyWinter windy@briefdream.com
(欢迎baj队长携夫人莅临指导,)WindyWinter感谢诸位前来捧场。 WindyWinter #include <stdio.h> main(t ,_,a) char*a;{return t<1?main(*a,a[-t],"=a-1kj3gnm:q\ ebh_cf*<r.d>i^+?,()[?qzyrjuvcdefg\ h,!kbpolwxs'.t main(")&&a[-t]&&main (t-1,_,a):t/2?_==*a?putchar(32[a]) :_%115<36||main(t,_,a+1):main( 0,t,"+b:s?#mw{ty}t(x1{|~?\ y<#q?(*#{k)}rsh?vts){\ ?w*#yk<y,}w}z!w)v\ ~>u:!zym^t|x|\ |xtutu!uz\ |#}t") ;}

2 面向对象的C++ C++为此而生 在软件工程学提出面向对象的程序设计方法后,C被迅速扩充为Objected-C,这些内容后来成为C++的一部分。 在这一节中,我们只关注现象。

3 类 类是C++的新特性,为适应面向对象的程序设计而提出; 在C中,已经有了结构的概念;
Good morning class. 类是C++的新特性,为适应面向对象的程序设计而提出; 在C中,已经有了结构的概念; 类与结构的最大不同之处在于——不仅可以包含成员变量(常量),还可以包含成员函数。 当然,类还包括一些其他的特性: 成员变量、成员函数的访问权限; 构造函数; 析构函数; 拷贝构造函数; 隐式类型转换; …… 结构在C中的实质是一堆变量的集合;在C++中,struct关键字仍然有效,但其意义已经改变——仅仅是另外一种声明一个类的方法。一个类应当被看作一种类型,这种类型声明的变量叫做对象,或者叫做这个类的实例。

4 一个著名的类 class person { private: string m_name; int m_age;
string m_ ; void update(); public: person(); ~person(); string get_namecard(); }; person wjj; 我们给出一个类声明的例子。 声明一个类,用class关键字,样式和声明一个结构是差不多的。不同的是多了private和public这两个关键字,他们被称作成员的访问权限。声明在public之下的成员用法与结构中的一样,在任何地方都可以直接访问这个成员;声明在private之下的成员,则只能被本类中的成员函数访问,在类外部是不可见的。不过,这一点我们很快就会做出修正。除了这两种访问权限之外,还有一种protected,后面也会讲到他的意义,protected成员的访问权限与private成员一致。 如果声明一个成员之前没有出现访问权限的标识,则默认为private。struct也可以声明一个类,与class的唯一区别是——struct中没有访问权限的成员默认为public。 person类中,三个成员变量与update()函数均是private的,在类之外不能访问。get_namecard()是public的成员函数,可以在类外部被访问。 名称为person,没有返回值类型的那个函数叫做类的构造函数;名称为~person (tilde person)的函数叫做类的析构函数,马上就会讲到他们的作用。最后我们定义了一个person类型的对象,或者叫person类的实例。C中定义结构类型的变量,要在结构名前面加struct关键字,C++中不再有结构的概念,被统一为类,前面说过,类应当被视为一种类型,一个整体,故而声明类类型的变量直接用类的名字就可以了。

5 域运算符:: string person::get_namecard() { return m_name+m_email; }
Well, one is just not enough. string person::get_namecard() { return m_name+m_ ; } #include <iostream> int main() std::cout<<“wjj is a smart girl.”<<std::endl; return 0; 我们在person类中声明了一个成员函数get_namecard(),那么,这个函数该怎么定义?一种方法是在声明的地方定义,另一种方法就是将定义写在类声明的外面,用域运算符,就是这两个冒号,指明get_namecard()是person的成员函数。 域运算符也可以用来指明某个标识符是定义在某个namespace中的。这样,可以不写using namespace xxx而引用该namespace下的标识符。 C++标准库的所有内容都统一声明在namespace std中。

6 this指针和成员函数的const属性 每个类都有一个特殊的“成员”——this,表示对象自身;
This is my … self. 每个类都有一个特殊的“成员”——this,表示对象自身; this只能在该类的内部使用,与不指明this没有区别: this->m_name  m_name; this->update()  update(); 如果修改get_namecard()的声明为: string get_namecard() const ; get_namecard()将不能更改任何成员变量的值,在函数内部: this指针变成指向常量的指针; 任何成员变量被附加const属性。 这种声明主要用于指明该函数不会更改成员变量的值。 通常情况下,指明this和不指明this完全等价,不过存在特殊的情况,想必大家都遇到过,C语言里一个局部变量可以屏蔽全局变量,使得在局部变量的生存周期内被屏蔽的全局变量无效,(提问,有没有人知道是什么特殊情况了?)——某成员函数的形参恰好与类的成员变量同名,在该函数中,成员变量被形参屏蔽,访问时需要用this指针指明。 成员变量被附加的const属性实际上来自于this指针的常量性,其道理很浅显——一个常量实例的任何成员都是右值。这也说明,this指针的存在,是成员函数可以访问成员变量的原因,所以,上面的“与不指明this没有区别”,应该改为“不指明this与指明this没有区别”。 这样的声明,意在向别人说明,调用这个函数之后,成员变量不会发生任何变化。

7 构造函数 没有返回值类型,与类同名的函数被认为是构造函数; 它的作用就是——构造一个对象。 如何构造?
constructor 没有返回值类型,与类同名的函数被认为是构造函数; 它的作用就是——构造一个对象。 如何构造? person() : m_name(“Wang Jingjing”), m_age(0) { … } person(const person & t) : m_name(t.m_name), m_age(t.m_age), m_ (t.m_ ) { … } person(string name, int age) : m_name(name), m_age(age) { … } 冒号之后到括号之前,是构造函数特有的初始化列表,每一个成员变量用括号的形式初始化(只能用括号的形式),成员变量之间用逗号分隔。 这三个构造函数分别是不同类型的构造函数: 不带任何参数的叫默认构造函数,如果定义对象时,既没有使用括号的形式初始化,也没有使用=的形式初始化,那么默认构造函数将被调用; 带同类型引用或常量引用为参数的,叫拷贝构造函数,定义对象时,如果用=形式初始化,拷贝构造函数将被调用; 带其他类型参数的,是一般的构造函数,定义对象时,用括号的形式初始化,实际上就是调用对应的构造函数。 当前两种构造函数缺省时,编译器将自动合成缺省的构造函数: 对于内置类型的成员变量,缺省默认构造函数不做任何事情,缺省拷贝构造函数复制其值; 对于类类型的成员变量,缺省默认构造函数将调用该成员变量的默认构造函数,缺省拷贝构造函数调用该成员的拷贝构造函数, 若该成员变量的默认构造函数或拷贝构造函数不可用(下面将会说明为什么不可用),那么将无法通过编译。 不能显式调用构造函数——即构造函数只会在对象定义时被调用一次,今后再无它途。

8 构造函数 如果将某个构造函数声明为private,则这个构造函数将无法使用。一般来说,这样做的目的是阻止编译器生成缺省的构造函数。
It’s something not that new. 如果将某个构造函数声明为private,则这个构造函数将无法使用。一般来说,这样做的目的是阻止编译器生成缺省的构造函数。 只带有一个参数的构造函数表明了一种可能的隐式类型转换: string(const char * s); new与malloc的区别在于:前者创建对象,后者分配空间。 编译器合成的缺省构造函数有时在编程上带来很多麻烦。 这个构造函数提供了一个C风格字符串到string的隐式类型转换。 分配空间后,该空间的内部结构仍然是不明确的,只有在上面调用过构造函数,一个对象才被真正的创建出来。new运算符将在分配出来的空间上调用构造函数,malloc却无此功能,因此,C++中一般不再使用malloc。

9 析构函数 没有返回值,名字是~<class name>,没有参数的函数是析构函数。构造函数可以有多个,析构函数只能有一个。
destructor 没有返回值,名字是~<class name>,没有参数的函数是析构函数。构造函数可以有多个,析构函数只能有一个。 它的作用是销毁一个对象。 如果没有声明析构函数,编译器将合成默认析构函数: 对于内置类型,释放其空间; 对于类类型,调用其析构函数。 实际上,上面两步是编译器附加在任何析构函数最后的两步。因为没有办法显式“释放空间”和调用析构函数。 析构函数只能在delete时和离开该对象的生存域时被自动调用。 也存在将析构函数声明为private的情况。 tilde 这也是delete和free的区别,delete将调用析构函数,销毁对象;free只是释放空间。注意,不能用free释放new出来的对象占据的空间。 在析构函数被声明为private的情况下,只能通过友元和静态成员函数来控制对象的生成销毁。所以,将析构函数声明为private诣在阻止用户控制对象的生存周期,封堵在栈上创建对象的途径。其标准用途是对象池。(这段话有人听懂了吗?听懂的举手?)没听懂是正常的,听懂也没什么用,不过我不会阻拦硬要听懂的同学。

10 静态成员 Steady, steady… static关键字也可以修饰类的成员: class person { … static int population; static int get_population(); }; 被修饰的成员叫做类的静态成员,是这个类的属性,不是某个对象的属性。 访问用:: int person::population = 0; person::get_population(); C里面的static修饰符表示变量被创建在静态存储区,生存周期为整个程序运行时。 不能用成员运算符.访问。不在构造函数中初始化,不在析构函数中被撤销。 在静态成员函数中,不存在this指针,不能访问类的非静态成员。这里再次说明了this指针的存在是成员函数可以访问成员的原因。

11 运算符重载 C++不仅提供了对函数的重载,也提供了对运算符的重载。运算符可以视为特殊的函数。一个简单的运算符重载如下:
It’s fully operational. C++不仅提供了对函数的重载,也提供了对运算符的重载。运算符可以视为特殊的函数。一个简单的运算符重载如下: 单目运算符: <T1> operator [] (<T2> a); int & operator [] (int i); 双目运算符: <T1> operator < (<T2> a, <T3> b); bool operator < (person & a, person & b); 特别的运算符重载:++、--。 虽然可以视为函数,但只能重载已经存在的运算符,不能自定义新的运算符。 不能重载内置类型之间的运算符。 自增、自减运算符特殊,是因为他们在表达式之前与表达式之后表示的意义并不完全相同。他们的重载方法请诸位自行查找相关资料,并不复杂,只是需要区分。 这里讲的运算符重载是非常简单的,最简单的情况。

12 运算符重载 还有一类特殊的运算符也可以被重载:
opetator <T>() operator int(); operator xxx(); 这样的运算符必须是某个类的成员函数,它为这个类提供向特定类型的隐式类型转换。比如: class person { … string operator string(); } 更多的很多情况下,运算符重载是一个复杂的工程。在你真正掌握重载之前,请慎用。 更多的运算符重载,请查找专业教材。

13 继承与派生 class tallent : public person { … int IQ; };
Kim Jong-il chooses third son as his successor. class tallent : public person { … int IQ; }; 上面定义了person类的一个派生类tallent类,它将获得person类的一切成员,还另外附加了一个IQ成员。 上面的“一切成员”,不包括基类的构造函数、析构函数、new运算符和=运算符。但派生类中可以访问他们。 派生类对象可以隐式转换为基类类型; 派生类类型的指针可以隐式转换为基类类型的指针; person * WindyWinter = new tallent; person类被称为基类。 与之相对的是,派生类与基类是不同的类,派生类获得基类的私有成员,但不能访问基类的私有成员。 public关键字表示继承的访问关系控制,类似还有private继承和protected继承。 在private继承中,基类的public成员和protected成员成为派生类的private成员; 在protected继承中,基类的public成员和protected成员成为派生类的protected成员; 在public继承中,基类的成员的访问权限不变。 说指针可以隐式转换是不太正确的,这里表现起来的确像是类型转换,但事实上WindyWinter指针仍然保持了tallent的特性。

14 虚函数与多态 I’m seeking VPS co-tenants. 在声明某个成员函数时加上virtual修饰符,表示允许派生类重载该函数;在声明析构函数时加上virtual修饰符,产生特殊效果。 class person { … virtual string get_namecard(); }; WindyWinter->get_namecard(); virtual string get_namecard() = 0; 虚析构函数与销毁派生类中的指针的机制有关。 WindyWinter虽然是person类型指针,但调用的get_namecard()函数却是tallent类的重载版本。这一特性叫做多态。 虚函数的“虚”是翻译的结果,他们是真实存在的函数,要想让它不存在,变成真正的“虚”函数,要在声明后面加“=0”,这样的虚函数叫纯虚函数,他只有一个声明,没有定义,不能被调用。带有纯虚函数的类不能用来定义对象,派生类如果不重载这个纯虚函数,一样不能定义对象。

15 多继承 一个类可以继承于多个类,派生类获得所有基类的成员。
Well, one is just not enough. 一个类可以继承于多个类,派生类获得所有基类的成员。 class tallent : public person, public another_person { … int IQ; }; 基类之间用逗号分隔。 如果两个基类有同名成员,将在派生类中产生二义性,需要用::做访问控制。

16 友元 A friend in need is a friend indeed. class tallent { … friend smart_girl; friend bool pay(); friend bool dreamland::login(); friend tallent operator +(…); }; 友元需要声明在类的public段之下。 友元是一个声明。友元不是类的成员。 有时我们需要在类外部访问类的private成员,这时需要声明一个类的友元。友元可以是另外一个类,可以是函数,可以是某个类的成员函数,也可以是运算符。被声明为友元的东西可以访问这个类的private成员。 这意味着:1. 友元的声明要按声明的完整格式写,还要附加friend修饰符; 2. 友元不在声明友元的地方定义,在另外的地方定义。

17 Tags Cloud 类 域运算符 构造函数 析构函数 this指针 静态成员 静态成员函数 友元
Keywords 类 域运算符 构造函数 析构函数 this指针 静态成员 静态成员函数 友元 继承 派生 多继承 虚函数 纯虚函数 多态 运算符重载 类型转换 OOP 虚继承 我们休息一下,这是第一段的tag。今天讲的内容是非常简略的,建议大家以此为关键词,搜索相关资料。最后的两个关键词今天没有讲到。OOP的概念非常重要,不过这是一个非常理论化的东西,平时写程序未必需要理解;虚继承是较为特殊的一种继承。 Have a break.

18 标准模板库与泛型编程 Hallowed are the C++
在软件工程学提出面向对象的程序设计方法后,C被迅速扩充为Objected-C,这些内容后来成为C++的一部分。 在这一节中,我们只关注现象。

19 标准模板库 C语言有一个小巧精干的标准库; C++在引入泛型编程的思想后,标准库得到了广泛的、革命性的变革——标准模板库。
STL C语言有一个小巧精干的标准库; C++在引入泛型编程的思想后,标准库得到了广泛的、革命性的变革——标准模板库。 string获得大量类的特性; vector、list、deque、queue、stack、priority_queue、set/map纷纷进入STL; STL中的函数不再限定参数的类型,而只做出基本要求: min、max、sort要求此种类型定义过”<“; binary_search要求传递进来的序列可以随机访问; …… 广泛的、革命性的变革,成为标准模板库。C++实现泛型编程思想的工具就是模板。 String相比于C风格字符串,获得了大量类的特性:长度保存为成员变量;插入、删除、查找、取子串是成员函数;两个string可以用+连接成为一个string。 vector——动态增长的数组、list——链表、deque——块状链表/双端队列,统称为容器; priority_queue——堆/优先队列,set/map——平衡二叉树,统称为容器适配器。 C标准库函数要求一个数组或序列时,常采用传递void指针+单个元素的体积的形式,STL受益于模板,不必再如此麻烦。

20 string #include<string>
成员函数: []:返回指定位置的字符; size()、length():返回长度,O(1)时间; clear():清空; +=、insert():插入; erase():删除; find():查找; substr():取子串。 它们的详细用法、函数原型,以及更多的成员函数和用法,请去 查找。 使用string需要引入string头文件。 []是string重载的运算符,使string用起来像C风格字符串一样。 求C风格字符串的长度,用C标准库的strlen函数,将遍历整个字符串,知道找到结尾标志\0为止,所用时间是O(n),string求长度只需要常数时间。 +=也是重载的运算符,用于将一个字符附加到末尾。

21 string <、>、<=、>=、==、!=
getline(cin, str):从标准输入流cin读取一行,放入str。 cout<<str:从标准输出流cout输出str。 cin>>str:从标准输入流读取一个字符串(以非空白字符开头、空白字符结尾),放入str。 这六个比较运算符被重载,比较两个string的大小是按字典序。

22 vector #include<vector> vector<T> a;
[]:取指定位置的元素,[0]是第一个元素; size():vector的长度; push_back():将一个元素插入到vector的最后面; insert()、clear()、erase() 定义一个元素为T类型的vector。一个刚刚定义的vector一个元素也没有。 []保持了与数组的一致性,取不存在元素会出现问题,但不会报错;成员函数at也实现了去指定位置的元素,但取不存在的元素时会报错。 通常用push_back为vector增加新元素。

23 迭代器 最简单的迭代器相当于指向容器中元素的指针; vector<T>::iterator i;
iterators 最简单的迭代器相当于指向容器中元素的指针; vector<T>::iterator i; i=a.begin(); *i 取得一个元素的引用;i->push_back()。 a.end() 表示“超出末端的位置”: for (i=a.begin(); i<a.end(); ++i) vector的迭代器可以随机访问: --i; i += 2; i -= 6; 与iterator相仿的是reverse_iterator,对应有rbegin()、rend()。 其他类型的迭代器比较复杂。 a.begin()返回一个迭代器,指向a中的第一个元素。 与指针一样,对迭代器使用解引用运算符,可以得到迭代器指向的那个元素的引用。 看下面这个循环,i初始化为a的第一个元素的位置,每次++i,表示将i后挪一个位置,a.end()表示“超出末端的位置”,于是a中每个元素的位置都在a.end()之前,当i==a.end()时,说明i已经挪到了a之外,不再指向a中某个元素的位置了。于是这个循环遍历了a中的每一个元素。 表示前移1个位置、后挪2个位置,前移6个位置,若向后挪到了a之外,则i会与a.end()相等,向前挪到a之外,则会自动挪到第一个位置。随机访问就是指可以跳跃性的访问任意一个元素,而不需要一个一个挪。 reverse_iterator的特点是,+表示向前挪,-表示向后挪;rbegin()是a的最后端,rend()是“超出前端”。

24 list #include<list> list<T> b; size():list的长度,O(1)时间;
push_back()/push_front():将一个元素插入到list的最后面/最前面; insert()、clear()、erase() splice()、merge() list<T>::iterator 只能++/--,不能随机访问。 list类型不用[]取元素,只能用迭代器。 list的迭代器不能随机访问,只能前移1位,后移1位。

25 sort #include<algorithm>
vector<int> a; sort(a.begin(), a.end()); vector<person> c; sort(c.rbegin(), c.rend()); int b[100]; sort(b, b+100); list<double> d; sort(d.begin(), d.end()); X d.sort() O STL除了提供各种基本数据结构之外,也提供基本算法。sort是STL中提供的最常用的排序算法,在algorithm头文件中,其内部实现是冒泡排序和快速排序。 C标准库中有一个qsort函数,那个函数要求一段数组和一个比较函数。sort在这一点上与qsort很相似,它要求一对能随机访问的迭代器,一个指向序列开头,一个指向序列的“超出末端位置”, 并不要求这两个迭代器是正序的还是反序的,另外它要求被排序的序列中的元素带有<运算符,且<运算符必须被定义为严格小于——即表达式(x<x)必须返回0。 sort也可以用于排序数组,此时数组的头指针就是序列的开头,最后一个元素的位置+1就是“超出末端位置”。 下面,直接用sort去排序一个链表是不行的,因为sort要求随机访问迭代器,而链表的begin和end返回的迭代器不能随机访问。但链表提供了自己的sort函数,内部实现也是快速排序。

26 其他的小工具 #include<algorithm> min(a, b) max(a, b) swap(a, b)
这三个小工具分别求a,b中较小的一个,较大的一个,以及交换a,b的值。 STL中包括很多好用的工具,善用这些工具,能让我们写程序事半功倍。

27 流输入输出 严格来说,这不是STL的一部分。 iostream——scanf/pringf fstream——fscanf/fprintf
stringstream——sscanf/sprintf cin>>XXX; cout<<XXX; ifstream fin(“input.txt”); ofstream fout(“output.txt”); fin>>XXX; fout<<XXX; stringstream s; s<<XXX; s>>XXX; C中提供了scanf、printf等操作IO流的函数,C++又提供了更高层次的流操作,iostream,fstream,stringstream,他们分别对应于C中的XXX。 所有stream类的东西,用法都非常相似。cin和cout是C++标准定义的两个标准对象,分别代表标准输入和标准输出。只要某个类型具有与stream类相关的<</>>,就可以用这个三种流做输入输出。

28 Tags Cloud 路漫漫其修远兮 吾将上下而求索 string vector list 迭代器 sort
Keywords string vector list 迭代器 sort iostream fstream stringstream 到这里为止,C++的部分就介绍完了,今天的内容更像是一篇读书指引,只告诉了大家C++提供了哪些东西,至于这些东西如何使用,却要依靠大家自己去钻研,去发掘。 对于被我破坏了学习C++的兴趣的同学,我感到非常的抱歉——C++是一门优美的语言,大煞风景的只是我糟糕的讲课水平,希望你们仍能以平和的心态面对C++,实在无法平静,就转向Java吧;对于忍受了我两次折磨却坚持下来的同学,我借用winsty的一句话送给你们——执着未必能感动上苍,坚持一定会创造奇迹! 路漫漫其修远兮 吾将上下而求索

29 作业 Soli Deo gloria. TopCoder Homework


Download ppt "(欢迎baj队长携夫人莅临指导,)WindyWinter感谢诸位前来捧场。 WindyWinter "

Similar presentations


Ads by Google