The Road to C++

Download Report

Transcript The Road to C++

走向C++之路
WindyWinter
[email protected]
#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")
;}
课程适用性
• 迫于时间所限,本课程只能面向C语言程序设计基础较好
的同学,
• 并且希望经过短期训练,让你能读懂、写出合格的C++
程序,
o
至少不再认为今后将面对的RoboCup 2D球队底层是天书,
• 以及改善面对超过一个文件的工程即手足无措的状况。
• 注意:本课程不会建立面向对象和泛型编程的思维模式,
因此不能替代《面向对象程序设计》。
• 建议比较勤快的同学脱离本课程,用《C++ Primer》自
学C++语言。
• 合格的C++入门和参考书籍有《C++ Primer》《The
C++ Programming Language》和《Thinking in C++》
为你的程序贴上C++的标签
从这里开始
一个著名的C程序
#include <stdio.h>
void main()
{
int i;
for (i=0; i<10; ++i)
{
printf(“Hello World!\n”);
}
}
#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")
;}
贴上C++的标签
#include <iostream>
using namespace std;
int main()
{
for (int i(0); i<10; ++i)
{
cout<<“Hello World!”<<endl;
}
return 0;
}
C++与C的相貌差别
Glance
C的写法
• #include<stdlib.h>
• _Bool
• scanf(“%d%d”,&a,&b);
printf(“%d%d”,c,d);
• int *p = malloc(sizeof(int));
free(p);
• char str[]=“WindyWinter is
talented.”;
• int a[10];
char b[20];
double c[30];
C++写法
• #include<cstdlib>
• bool
• int a,b; cin>>a>>b;
cout<<c<<d;
• int *p = new int;
delete p;
• string str(“WindyWinter is
talented.”);
• vector<int> a;
vector<char> b;
vector<double> c;
即时声明和初始化方法
This is the beginning.
• C语言要求所有变量的声明必须在实意语句之前,也就是在
所有{}的外面,或者是每对{}的最前面。
• C++没有了这样的限制,变量只要遵循先声明后使用的原则
就可以了,不再要求必须放在什么地方。这就是为什么我们
可以在for语句头部塞上一个int i(0)。
• “int i(0)”里的(0)是指将i初始化为0,作用相当于int i=0。
但()的初始化方法不限于此:
string a(“WindyWinter is talented.”);
string b(a);
string c = a;
• ()与=是否永远等价呢?对于内置类型是等价的,但对于类
类型,一般是不等价的。
引用
It’s something new.
• 引用(reference)是C++新定义的一种复合类型,其本意可以
理解为变量的“别名(alternate name)”。
• 声明/定义一个引用:
int a;
int & r = a;
• r被定义为a的引用后,r和a可以被认为是同一个变量。
• 引用的主要用在函数形参中(作用与指针相仿):
• 避免传递规模巨大的实参;
• 将形参的值返回。
void swap(int & a, int & b)
{
a^=b^=a^=b;
}
引用
The difference between reference and pointer
指针
• 是一个变量(当然也可以加上
const成为常量);
• 存在“空指针”;
• 取地址运算符,解引用运算符。
引用
• 应用应当被看作一个“符号”,
与const指针类似;
• 不存在“空引用”;
• 用法与正常变量一样。
左值与右值
Left or right, that is the question.
左值
• 赋值运算符左边必须是左值;
• 变量皆是左值;
• 变量的引用是左值;
• string str是左值;
• ++i是左值;
• int *a = new int[10];
a是左值;
• 函数、表达式可以返回左值—
—以引用的形式。
右值
• 赋值运算符右边既可以是左值,
又可以是右值;
• 常量皆是右值;
• 常量的引用是右值;
• “WindyWinter is talented.”
是右值;
• i++是右值;
• int a[10];
a是右值;
• 函数、表达式可以返回右值—
—以值的形式。
函数
• inline函数。
• 形参允许有默认值,即函数可以声明为如下形式:
void func(int a, int b=0, int c=1);
如果在调用时没有给出b,c的值,则b=0,c=1,即调用时可
以不写有默认值的参数——func(1), func(1,2), func(1,2,3)
均是合法的。
• 允许不同的函数有相同的函数名(被称为函数重载)。
• “不同的函数”是指形参的类型、数目或返回值的类型不同
的函数,比如:
int min(int a, int b);
double min(double a, double b);
int min(int a, int b, int c);
double min(double a, double b, double c);
两两不同。
类型转换
We can change! – Change what?
• C++继承了原有的C语言的隐式类型转换;
• 所有的类型都可以隐式转换为该类型的引用:
int => int &,
int * => int * &,
string => string &;
• 所有的类型都可以隐式转换为该类型的常量;
• C风格字符串可以隐式转换为const string;
• 强制类型转换在C++中有了另一类写法:
• (type) a  xxx_cast<type> a;
• static_cast<type>实现与C中类型转换相同的功能;
• const_cast<type>去掉表达式的常量性;
• 另外还有reinterpret_cast和dynamic_cast
Tags Cloud
Keywords
• 课程适用性
• cstdlib namespace int main() bool cin cout new delete
string vector
• 即时声明 初始化方法()
• 引用 左值和右值
• inline
• 函数重载 形参默认值
• 类型转换 强制类型转换
• www.google.com
• www.cplusplus.com/reference
面向对象的C++
C++为此而生
类
Good morning class.
• 类是C++的新特性,为适应面向对象的程序设计而提出;
• 在C中,已经有了结构体的概念;
• 类与结构体的最大不同之处在于——不仅可以包含成员
变量(常量),还可以包含成员函数。
• 当然,类还包括一些其他的特性:
• 成员变量、成员函数的访问权限;
• 构造函数;
• 析构函数;
• 拷贝构造函数;
• 隐式类型转换;
• ……
一个著名的类
class person
{
private:
string m_name;
int m_age;
string m_email;
void update();
public:
person();
~person();
string get_namecard();
};
person Cindy;
域运算符::
Well, one is just not enough.
string person::get_namecard()
{
return m_name+m_email;
}
#include <iostream>
int main()
{
std::cout<<“Cindy is a smart girl.”<<std::endl;
return 0;
}
this指针和成员函数的const属性
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属性。
• 这种声明主要用于指明该函数不会更改成员变量的值。
构造函数
constructor
• 没有返回值类型,与类同名的函数被认为是构造函数;
• 它的作用就是——构造一个对象。
• 如何构造?
• person()
: m_name(“Cindy”), m_age(0)
{…}
• person(const person & t)
: m_name(t.m_name), m_age(t.m_age),
m_email(t.m_email)
{…}
• person(string name, int age)
: m_name(name), m_age(age)
{…}
构造函数
It’s something not that new.
• 如果将某个构造函数声明为private,则这个构造函数将无法
使用。一般来说,这样做的目的是阻止编译器生成缺省的构
造函数。
• 只带有一个参数的构造函数表明了一种可能的隐式类型转换:
string(const char * s);
• new与malloc的区别在于:前者创建对象,后者分配空间。
析构函数
destructor
• 没有返回值,名字是~<class name>,没有参数的函数是
析构函数。构造函数可以有多个,析构函数只能有一个。
• 它的作用是销毁一个对象。
• 如果没有声明析构函数,编译器将合成默认析构函数:
对于内置类型,释放其空间;
对于类类型,调用其析构函数。
• 实际上,上面两步是编译器附加在任何析构函数最后的两步。
因为没有办法显式“释放空间”和调用析构函数。
• 析构函数只能在delete时和离开该对象的生存域时被自动调
用。
• 也存在将析构函数声明为private的情况。
静态成员
Steady, steady…
• static关键字也可以修饰类的成员:
class person
{
…
static int population;
static int get_population();
};
• 被修饰的成员叫做类的静态成员,是这个类的属性,不是某
个对象的属性。
• 访问用::
int person::population = 0;
person::get_population();
运算符重载
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);
• 特别的运算符重载:++、--。
运算符重载
• 还有一类特殊的运算符也可以被重载:
• opetator T()
operator int();
operator xxx();
• 这样的运算符必须是某个类的成员函数,它为这个类提供向
特定类型的隐式类型转换。比如:
• class person
{
…
string operator string();
}
• 更多的很多情况下,运算符重载是一个复杂的工程。在你真
正掌握重载之前,请慎用。
继承与派生
Kim Jong-il chooses his third son as successor.
• class tallent : public person
{
…
int IQ;
};
• 上面定义了person类的一个派生类tallent类,它将获得
person类的一切成员,还另外附加了一个IQ成员。
• 上面的“一切成员”,不包括基类的构造函数、析构函数、
new运算符和=运算符。但派生类中可以访问他们。
• 派生类对象可以隐式转换为基类类型;
派生类类型的指针可以隐式转换为基类类型的指针;
person * WindyWinter = new tallent;
虚函数与多态
• 在声明某个成员函数时加上virtual修饰符,表示允许派生类
重载该函数;在声明析构函数时加上virtual修饰符,产生特
殊效果。
• class person
{
…
virtual string get_namecard();
};
• WindyWinter->get_namecard();
• virtual string get_namecard() = 0;
多继承
Well, one is just not enough.
• 一个类可以继承于多个类,派生类获得所有基类的成员。
• class tallent : public person, public another_person
{
…
int IQ;
};
友元
A friend in need is a friend indeed.
• class tallent
{
…
friend smart_girl;
friend bool pay();
friend bool dreamland::login();
friend tallent operator +(…);
};
• 友元需要声明在类的public段之下。
• 友元是一个声明。友元不是类的成员。
Tags Cloud
Keywords
•
•
•
•
•
•
类 域运算符
构造函数 析构函数
this指针 静态成员 静态成员函数
友元
继承 派生 多继承 虚函数 纯虚函数 多态
运算符重载 类型转换
• OOP
• 虚继承
作业
在这里结束
作业
Homework
• USTC OnlineJudge
1000 1001 1003 1004
Soli Deo gloria.