标题@TOC
C++编程学习
课程概述
面向对象的方法简介
对象----存在即合理
抽象性–物以类聚
封装----事物的封闭性
继承----事物的相似性
多态----事物的多样性
面向对象方法(类与对象)
C++概述
1.什么是C++?
C to C++:
* C语言的超集
* 面向对象编程
* 可移植性,不牺牲性能和底层功能
C++ compiler:将C++代码翻译为C代码
2.C++适合?
* 算法
* 应用开发
* C/C++服务器
C++设计原则
- C++设计成使用静态型别机制、和C同样高效且可移植的多用途程序设计语言。
- C++设计成直接的和广泛的支持多种程序设计风格(程序化程序设计、数据抽象化、面向对象程 序设计、泛型程序设计)。
- C++设计成给程序设计者更多的选择,即使可能导致程序设计者选择错误。
- C++设计成尽可能与C兼容,借此提供一个从C到C++的平滑过渡。
- C++避免平台限定或没有普遍用途的特性。
- C++不使用会带来额外开销的特性。
- C++设计成无需复杂的程序设计环境。
UNIX铁律: K.I.S.S-Keep It Simple,Stupid!
&参考书籍
C++大学教程(推荐)
C++Primer
C++编程思想
&开发环境
Visual Studio 201X Community
Code::Block
Qt Creator
C++的第一个程序
第一个C++程序:
#include<iostream>
int main(void)
{
std::cout<<"hello world!"<<std::endl;
return 0;
}
流的概念及用途
- C++的I/O是以字节流的形式实现的,流(stream)实际上就是一个字节序列。
- 输入流:在输入操作中,字节从输入设备(如键盘、磁盘、网络连接等)流向内存;
- 输出流:在输出操作中,字节从内存流向输出设备(如显示器、打印机、磁盘、网络连接等);
- 这里“流”试图说明字符随着时间顺序生成或消耗的。
- 输人/输出系统的任务实际上就是以一种稳定、可靠的方式在设备与内存之间传输数据。
- C++并没有直接定义进行输入输出的任何语句,这些功能是由标准IO库中的 来完成。
命名空间(namespace)
实际上就是一个由程序设计者命名的内存区域,程序设计者可以根据需要指定一些有名字的空间域,把一些全局实体分别放在各个命名空间中,从而与其他全局实体分隔开来。
命名空间是ANSIC++引入的可以由用户命名的作用域,用来处理程序中常见的同名冲突。
std::cout std::cin std::endl
+使用命名空间:
using std::cout;
using namespace std;
#include<iostream>
using namespace std;//使用命名空间
int main(void)
{
cout <<"hello world!"<<endl;
return 0;
}
C++程序的执行过程
1.C++主要有三个编译阶段:预处理、转译成目标代码和链接(最后的两个阶段一般才视为真正的“编译”)。
2.C++预处理命令和C基本相同
课堂练习: C/C++的字符串比较
0.C语言字符串练习 char st[100];
0.C++语言字符串练习 string str;
1.检测字符串长度 int len = strlen(st);
1.检测字符串长度 int len = str.length();
2.字符串比较 strcmp(st1,st2);
2.字符串比较 str1.compare(str2);
3.在已有字符串后追加新串 strcat(st1,st2); strncat(st1,st2,n);
3.在已有字符串后追加新串 str1 += str2; str1.append(str2);
4.字符串拷贝 strcpy(st1,st2); strncpy(st1,st2,n);
4.字符串拷贝 str2 = str1; str2 = str1.substr();
5.字符串查找 where = strchr(st,ch)
5.字符串查找 where = str1.find(str2);
#include<iostream>
#include<string>
using namespace std;
int main(void)
{
string str("hello");
cout << str.length() << endl;
str.append(" world!");//在已有字符串后追加新串
cout << str << endl;
return 0;
}
C/C++数据类型与变量
C/C++变量
程序运行过程中值能否发生改变分为**常量和变量**
从变量作用域的大小考虑:全局变量,局部变量
全局变量:定义在所有的函数体之外,它们在程序开始运行时分配存储空间,在程序结束时释放存储空间
函数中定义的变量称为局部变量(Local Variable)
从变量的生命周期考虑: 静态生存周期和动态生存周期
动态存储变量:变量仅在需要的时候分配和占用内存
静态存储变量:变量在程序运行过程中占用固定的内存
从变量的在内存中位置考虑:普通变量与指针变量
动态内存分配
所谓动态内存分配是指在程序运行期间根据实际需要随时申请内存,并在不需要时释放
new/delete是C++的运算符
用于申请动态内存和释放内存
new运算符的语法格式为:指针=new 数据类型;
• int *p=new int;
• int *p=new int[30];
delete运算符的功能是用来删除是用new创建的对象或一般类型的指针,其格式如下:delete<指针名>
使用delete也可以用来删除使用new创建的对象数组,其使用格式如下:delete [] 指针名
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
char *str;
str = new char[10];
strcpy(str,"hello");
cout << str << endl;
delete []str;
return 0;
}
杜绝“野指针”
-
指针用来存放某个变量的地址值的一种变量
-
“野指针”不是NULL指针,是指向“垃圾”内存的指针,“野指针”的危险之处在于if语句对它不起作用。
-
任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的。所以,指针变量在创建的同时应当被初始化,要不将指针设置为NULL,要么让它指向合法的内存。
char *p = NULL;
char * str = (char *)malloc(100); -
指针p被free或者delete之后,应设置为NULL。
-
指针操作超越了变量的作用范围
-
程序不能返回指向栈内存的指针或引用。
-
数组与指针
-任何一个数组的名字是一个常量指针,其值是该数组的首元素的地址值
-在C++中引用一个数组元素有两种等价的方法:
-下标法:如a[j]或p[j]形式,比较直观
-指针法:如*(a+j)或 *(p+j)形式,比较高效
-数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅是一个指针。
-指向数组的指针则是另外一种变量类型(在linux或win32平台下,长度为4),仅仅意味着数组的存放地址; -
数组指针与指针数组
总结
- new运算符根据对象的类型,自动决定其大小,不使用sizeof运算符,而malloc要指定分配存储空间的大小;
- new返回指向此类型的指针,不用进行强制指针类型转换。malloc返回指向void*类型的指针。
- 如果在申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败
- 用free或delete释放内存之后,没有将指针设置为NULL。导致产生“野指针”。防止使用指针值为NULL的内存。
- 动态内存的申请与释放必须配对,防止内存泄露。
引用与函数传参
什么是引用?
- 引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
- 引用(&)在此是起标示作用而不是求地址运算,定义格式
<类型>&<引用名>(<变量名>);
<类型>&<引用名>=(<变量名>);
int a = 3;
int &m = a; //定义引用并初始化
int n = m; //将引用赋给变量
int *p = &m;
m = m + 5; // a = 8,对引用的操作就是对被引用者的操作
引用详解
初始化与赋值
- 定义引用时必须初始化;
- 可以将一个引用赋予给某个变量;
- 引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
指针和引用的区别 - 指针通过地址间接访问某个变量
- 引用通过别名直接访问某个变量
- 引用一般用作函数的参数或函数的返回值
- 如果既要利用引用提高使用效率,又要保护传递给函数的数据不在函数中被改变,就应当使用常引用。
引用的用途
- C语言中没有引用,C++中才有引用,引用的主要用途就是在函数传参和返回值上。
- 使用引用作为传递函数的参数,在内存中并没有产生实参的副本,他是直接对实参操作。
- 如果输入参数以值传递的方式传递对象,则宜改用“const &”方式来传递,这样可以省去临时对象的构造和析构过程,从而提高效率。
- 如果既要利用引用调高使用效率,又要保护传递给函数的数据不在函数中被改变,就应当使用常引用。
- 如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传递”可以提高效率,有些场合不可以。
常引用
- 声明一个引用不是新定义一个变量,他只是该引用名是目标变量的一个别名,它本身不是一种数据类型,因此引用不占存储单元。故对引用取地址,就是对目标变量取地址
- 非const引用只能绑定到该引用同类型的变量。const引用则可以绑定到不同但相关类型的对象, 或着绑定到右值。
- 常引用的声明方式:
const 类型标示符 &引用名 = 目标变量名; - 用这种方式声明的引用,不能通过引用对变量的值进行修改,但目标自身任然能够改变,这是语言的不一致性。
#include<iostream>
using namespace std;
int main()
{
double dvalue = 3.14;
const int &pd = dvalue;
dvalue = 50.6;
cout << pd <<"\n"<<dvalue <<endl;
return 0;
}
函数返回一个类型的引用
注意:不允许返回的引用对应于一个局部变量
#include<iostream>
using namespace std;
int &getlnt(const int v)
{
int *p = new int(v);
return *p;
}
int main()
{
int &n = getlnt(123456789);
cout << n << endl;
int *pp = &n;
delete pp;
return 0;
}
return语句不可返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。
C/C++函数说明
C++函数说明
- C/C++中的函数说明:
- 四个组成部分:返回类型、函数名、形式参数表、函数体
- 函数的申明与实现,函数的调用与传参(形参与实参)
- 函数与指针:指针函数与函数指针
- C++增强了函数类型:
- 基础函数:内联函数,函数重载,模板函数(泛型编程)
- 成员函数:构造/析构函数,常成员函数,静态成员函数,虚函数
内联函数(inline)
- 内联(内嵌)函数,主要解决的是程序的运行效率问题。
- 解决手段,在编译阶段,编译器就会把每次调用内联函数的地方都替换为该函数体内的代码。
- 引入原因:解决程序中一些函数体代码不是很大,却有被频繁调用的函数的函数调用效率问题。
- 解决方法: 以目标代码的增加为代价来换取时间上的节约。
- 当在一个函数定义或声明前加上关键字inline,则把该函数定义为内联函数。
#include<iostream>
using namespace std;
inline int f(int x)
{
return x * x;
}
int main()
{
int x(2);
cout << f(x) << endl;
cout << f(x + 1) <<endl;
return 0;
}
使用内联函数的注意事项
- 内联函数的定义必须出现在该函数第一次被调用前;
- 内联函数不能有复杂的控制语句,如switch,goto和while。
- 递归函数不能是内联函数。类结构中所有的在类说明内部定义的函数都是内联函数。
- 内联函数具有与带参的宏定义#define相同的作用和相似的机理,但内联函数具有宏定义所没有的优点而没有缺点,它消除了宏定义的不安全性。
函数重载
- 函数重载又称为函数的多态性
- 指同一个函数名对应着多个不同的函数。
- “不同”是指这些函数的形参表必须互不相同,或者是形参的个数不同,或者是形参是类型不同,或者是两者都不相同,否则将无法实现函数重载。
- 下面是合法的重载函数:
int funcl (int ,int);
int funcl(int );
double funcl(int,long);
double funcl(long);
#include <iostream>/*函数重载*/
using namespace std;
int add(int,int);
double add(double,double);
int main()
{
cout<<add(10,20)<<endl;
cout<<add(10.0,20.5)<<endl;
}
int add(int x,int y)
{
cout<<"int"<<endl;
return x+y;
}
double add(double x,double y)
{
cout<<"double"<<endl;
return x+y;
}
注意:
-
重载函数的类型,即函数的返回类型可以相同,也可以不同。但如果仅仅是返回类型不同而函数名相同、形参表也相同,则是不合法的,编译器会报“语法错误”。如:
int funcl (int a,int b);
double funcl (int a,int b); -
除形参名外都相同的情况,编译器不认为是重载函数,只认为是对同一个函数原型的多次声明。
-
在调用一个重载函数funcl()时,编译器必须判断函数名funcl到底是指哪个函数。它是通过编译器,根据实参的个数和类型对所有funcl()函数的形参一一进行比较,从而调用一个最匹配的函数。
带默认参数值的函数
- 在C++语言中调用函数时,通常要为函数的每个形参给定对应的实参。若没有给出实参,则按指定的默认值进行工作。
- 当一个函数既有定义又有声明时,形参的默认值必须在声明中指定,而不能放在定义中指定。只有当函数没有声明时,才可以在函数定义中指定形参的默认值。
- 默认值的定义必须遵守从右到左的顺序,如果某个形参没有默认值,则它左边的参数就不能有默认值。如:
void funcl(int a,double b=4.5,int c=3);//合法
void funcl(int a=1,double b,int c=3);//不合法
在进行函数调用时,实参与形参按从左到右的顺序进行匹配,当实参的数目少于形参时,如果对应位置形参又没有设定默认值,就会产生编译错误;如果设定了默认值,编译器将为那些没有对应实参的形参取默认值。
注意
- 形参的默认值可以是全局常量、全局变量、表达式、函数调用,但不能为局部变量。例如,
- 下例不合法:
void funcl()
{
int k;
void g(int x=k);//k为局部变量
} - 使用缺省参数时,主要满足函数重载条件;
void fun (int x, int y=0);
void fun(int x);
此时函数fun不能进行重载,因为编译器不能唯一确定调用哪个函数(fun(3)或fun(3,0)均可)。
##封装性
-
封装是面向对象的特征之一,是对象和类概念的主要特性。
-
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
-
具备封装性(Encapsulation)的面向对象程序设计隐藏了某一方法的具体执行步骤,取而代之的是通过消息传递机制传送消息给它。
-
封装
-
关键字:public、protected、prviate
• 修饰成员变量和成员函数;
• 继承时使用 -
破坏封装:关键字friend
• 友元类和友元函数
封装好的结构体
#include <iostream>
#include <string.h>
using namespace std;
struct Person
{
char *m_name;
int m_age;
void setName(const char*);
void setAge(int);
void disp();
};
void Person::setName(const char* name)
{
m_name = new char[100];
strcpy(m_name, name);
}
void Person::setAge(int age)/*作用域=作用域名+作用域符*/
{
m_age = age;
}
void Person::disp()
{
cout << "Name:" << m_name << endl;
cout << "Age:" << m_age << endl;
}
int main()
{
Person somebody;
somebody.setName("DaXian666");
somebody.setAge(23);
somebody.disp();
}
定义类来实现数据隐藏
#include <iostream>
#include <string.h>
using namespace std;
class Person
{
private://数据
char *m_name;
int m_age;
public://函数
void setName(const char*);
void setAge(int);
void disp();
};
void Person::setName(const char*name)
{
m_name = new char[100];
strcpy(m_name, name);
}
void Person::setAge(int age)
{
m_age = age;
}
void Person::disp()
{
cout << "Name:" << m_name <<endl;
cout << "Age:" << m_age << endl;
}
int main()
{
Person p;
p.setName("DaXian666");
p.setAge(23);
p.disp();
}
如何实现数据隐藏
-
引入class类型
-
对数据成员进行保护
-
增加存取范围
• 私有成员private
• 保护成员protected
• 公共成员public -
在C++中,class与struct的区别:
-
struct的缺省作用域为public
-
class的缺省作用域为private
-
类定义格式的构成
-
说明部分:说明类中的成员,包含数据成员的说明和成员函数的说明;
-
实现部分:对成员函数的定义
-
类的一般定义格式
类与对象 -
C++是为了解决编写大程序过程中的困难而产生的。
对象:客观世界中任何一个事物都可以看成一个对象( object )。
对象组成:
数据——描述对象的属性
函数——行为(操作代码),根据外界给的信息进行相应操作的代码外界给的信息进行相应操作的代码。
具有相同的属性和行为的对象抽象为类(class ) -
类是对象的抽象
-
对象则是类的特例
类与对象
类中的权限
- 一般的做法:将需要被外界调用的成员函数指定为public,它们是类的对外接口。
- 并非要求把所有成员函数都指定为public
- 有的函数并不是准备为外界调用的,支持其他函数的操作,就应该将它们指定为private 。
- 这种函数称为其他成员的工具函数(utility function),类外用户不能调用。
- 私有的成员函数只能被本类中的其他成员函数所调用,而不能被类外调用。
- 成员函数可以访问本类中任何成员(包括私有的和公用的),可以引用在本作用域中有效的数据。