技术交流:wulianjishu666
上午:C++概述
下午:C++基础
教学内容:
1、面向对象:程序=(对象+对象+…) 对象=(算法+数据结构)
2、类与对象:
对象是现实世界中的一个实体,其特征是:
• 每一个对象必须有一个名字以区别于其它对象
• 用属性(或叫状态)来描述它的某些特征
• 有一组操作,每一个操作决定对象的一种行为
类的精确定义:具有共性的实体的抽象
• 类是创建对象的样板,包含对创建对象的状态
描述和对操作行为的说明
例如: 黎明是一个老师
老师就是一个类
黎明是类中的一个对象;这个对象有名字、属性(男、年龄等等)、行为(教学、说话等)。
3、C++的输入、输出和编译
C++在linux下的编译是使用g++ mian.c -o main
与C语言不同,C++的头文件是:
#include
同时必须说明类型空间:
using namespace std;
或者指明:
//using std::cin; //输入
//using std::cout; //输出
//using std::endl; //结束线和\n类似
例如:头文件的说明
//*****************************************
#include
//*****************************************
#include
using namespace std;
//*****************************************
#include
using std::cin;
using std::cout;
using std::endl;
//******************************************
cin和cout使用的时候以下格式:
cin>>x>>y;
cout<
例如:
//*********************************************
float x,y,z = 1.2f;
cout<<"输入两个数:"; //输出到显示器
cin>>x>>y; // 从键盘输入
z = x+y;
cout<<“z = ”<
return 0;
//*********************************************
如果在输出时候要指定格式,就必须使用#include
iomanip的作用:
主要是对cin,cout之类的一些操纵运算子,
比如setfill,setw,setbase,setprecision等等。
它是I/O流控制头文件,就像C里面的格式化输出一样.以下是一些常见的控制函数的:
dec 置基数为10 相当于"%d"
hex 置基数为16 相当于"%X"
oct 置基数为8 相当于"%o"
setfill( 'c' ) 设填充字符为c
setprecision( n ) 设显示有效数字为n位
setw( n ) 设域宽为n个字符 ,右对齐,如果实际长度大于设置长度,设置失效
setprecision(n)与setiosflags(ios::fixed)合用,可以控制小数点右边的数字个数
setiosflags(ios::fixed) 固定的浮点显示
setiosflags(ios::scientific) 指数表示
setiosflags(ios::left) 左对齐
setiosflags(ios::right) 右对齐
setiosflags(ios::skipws) 忽略前导空白
setiosflags(ios::uppercase) 16进制数大写输出
setiosflags(ios::lowercase) 16进制小写输出
setiosflags(ios::showpoint) 强制显示小数点
setiosflags(ios::showpos) 强制显示符号
例如:
//*********************************************************
int i=100;
cout<<"Dec i = "<<dec<
cout<<"Hex i = "<<hex<
cout<<"Oct i = "<<oct<
cout<<"i = "<<setiosflags(ios::fixed)<
cout<<setiosflags(ios::scientific)<<12345.0<
cout<<setprecision(3)<<12345.0<
cout<<setfill(‘@‘)<
//***********************************************************
4、在C++中,可以使用重复变量名
全局作用域运算符:“::”
例如:但是在编写程序中,最好不要使用相同变量名
//***********************************************************
int avar = 10; //全局变量avar
int main()
{
int avar = 20; //局部变量avar
cout<<"avar is: "<
avar = 25; //修改局部变量
cout<<"avar is: "<
cout<<"avar is: "<<::avar<
::avar = 30; //修改全局变量
cout<<"avar is: "<<::avar<
return 0;
}
//************************************************************
5、在C++中,结构体、枚举、联合体可以用类型名直接定义类型
例如:
//************************************************************
enum BOOL{FALSE,TRUE};
struct STRING
{
char* prt;
int length;
};
int main()
{
BOOL e_1; //如果是C语言定义,必须是enum BOOL e_1;
STRING STR_1;
}
//**************************************************************
6、C++的宏定义
C++提供一种更灵活、更安全的方式来定义常量,即const修饰符。一般的语法格式:
const 类型名 常量名=常量值(表达式);
对于上例,用const定义为:
const int LIMIT = 100;
例如:
//**************************************************************
char name[20] = "muqiujuan";
char company[20] = "sunplusedu";
const char* p1 = name; //不可以使用p1[2]='o';可以使用p1=company;
char* const p2 = name; //不可以使用p2=company,可以使用p1[2]='o';
const char * const p3 = name;//两种方式都不可以使用
//**************************************************************
const是定义常量, 不是常量,实际上还是变量;修饰的部分是不能修改的,相当于常量。
const char* p1 = name;可以使用num[2]='o';来修改,只不要使用p1[2]的方式修改。
使用inline内联函数替代宏定义,可以消除宏定义的不安全性。内联函数具有宏定义的所有优点而没有缺点
每当程序中出现对该函数的调用时,C++编译器使用函数体内的代码替代函数调用表达式
例如:
//*****************************************************************
inline int inlineDoub(int x)
{
return x*2;
}
int main()
{
....
for(i = 1; i <= 3; i++)
{
cout<
}
}
//*****************************************************************
内联函数和宏定义有所区别,它是把调用函数的地方,直接使用代码替换掉,而不需要转换程序指针,所以效率很高;同时,也造成了如果内联函数如果代码很大,调用很多,在编译器编译时候可能会忽略inline的作用;所以一般内联函数都是很小的程序段,而且调用不是很多。
7、C++的缺省规则
C++在声明函数原型时,可为一个或多个参数指定缺省参数值,以后调用此函数,若省略其中某一参数,C++自动地以缺省值作为相应参数的值。
缺省规则的时候,必须从右到左缺省;
而赋值的时候是从左到右。
例如:
//*****************************************************************
void func(int x=10,int y = 40,int z = 49);
//void func(int x=10,int y ,int z = 49); //错误必须从右到左赋值
int main()
{
func(1,2,3); //x=1,y=2,z=3
func(1,2); //x=1,y=2,z=49
func(1); //x=1,y=40,z=49
func(); //x=10,y=40,z=49
return 0;
}
//*************************************************************
8、函数重载
在C++中,可以重复定义同名函数,只要定义的形参个数不同或形参的类型不同
例如:
//*****************************************************************
int mul(int x, int y, int z = 0) //有三个形参
{
cout<<"in int mul(int x,int y,int z = 0 )\n";
return x*y*z;
}
int mul(int x, int y) //有2个形参,都是整型
{
cout<<"in int mul(int x,int y) \n";
return x*y;
}
double mul(int x,double y) //有2个形参,一个整型和一个浮点型
{
cout<<"in double mul(int x,double y)\n";
return x*y;
}
int main()
{
int a = 3;
int b = 4;
double c = 5.66;
cout<//一个整型和一个浮点型,调用第三个
//错误,可以表示第一个(缺省情况下)或第二个
cout<三个形参,调用第一个
return 0;
}
//**************************************************************
9、强制类型转换
二种方法:
第一种C语言方法:(int)c
第二种方法:int(c)
例如:
//**************************************************************
double i = 10.12;
double j = 20.34;
int x,y;
x = (int)i; //C形式
y = int(j); //C++形式
//**************************************************************
10、动态内存申请和释放
new和delete运算符
分配内存:p = new 数据类型;
释放内存:delete p;
例如:
//*************************************************************
int* p, *q;
p = new int; //申请一个int,p指向该地址
q = new int(90); //申请一个int并给该地址存储内容赋初值90,p指向该地址,
*p = 10;
cout<< *p + *q <
cout<<"p= "<
cout<<"&p= "<<&p<
delete p,q;
char* p_str = NULL;
p_str = new char[100];
memset(p_str,'a',100);
p_str[99]='\0';
cout<
cout<<&p_str<
cout<<(void *)p_str<
delete p_str; //或者delete []p_str;
//************************************************************
使用new动态分配内存时,如果没有足够的内存时,将返回空指针(NULL),所以每次申请必须要判断是否申请成功。
11、引用(可以替代指针的作用)
引用的时候,必须在定义的时候初始化
例如:
//****************************************************************
int a;
int &b; //错误
b = a;
int a;
int &b = a; //正确
//**************************
使用引用可以替代指针,
例如:
//*******************************
void swap(int *m,int *n); //指针
void swap(int &m,int &n); //引用
int main()
{
int a = 10;
int b = 20;
swap(&a,&b); //指针方式调用
int aa = 10;
int bb = 20;
swap(aa,bb); //引用方式调用
cout<<"aa value = "<
<<", bb value= "<
return 0;
}
void swap(int *m,int *n)
{
int temp;
temp = *m;
*m = *n;
*n = temp;
}
void swap(int &m,int &n)
{
cout<<"in fun 'swap(int &m,int &n)'"<
cout<<"m addrress = "<<&m
<<", n addresss = "<<&n<
int temp;
temp = m;
m = n;
n = temp;
cout<<"swap(int &m,int &n) over"<
}
//*******************************
实际引用就相当于把地址传给了形参,是的形参操作的空间就是实参地址;而如果普通的调用仅仅把值复制给了形参,实参是不受影响的。
同样,利用引用可以以函数的返回值为引用,例如:
//*******************************
int a[] = {1,3,5,7,9};
int &index(int); //声明返回引用的函数
int main()
{
index(2) = 25; //a[2] =25;
return 0;
}
int& index(int i)
{
return a[i];
}
//****************************************************************
12、对象和类
对象=结构+算法
结构的定义和C语言有所不同,例如:
//***************************************************************
struct Point{
int x, y;
public:
void setXY(int w,int t)
{
x = w;
y = t;
}
void print()
{
cout<
}
};
//*************************
在C++中定义结构体有二种,
第一种是struct,默认是public
第二种是class,默认是private
结构体成员分二类,public和private:
public:除结构体内可以访问,外部也可以访问
private:只能结构体内部成员访问
例如:
//***************************
class Point{ //这个类,有二部分,一部分是数据成员,另一部分是成员函数
int x;
int y;
public:
void setXY(int w,int t)
{
x = w;
y = t;
}
void print(void)
{
cout<
}
}b,c; //b,c是全局对象
int main()
{
Point a; //具体的局部对象,是point类
//a.x = 11; //错误,外部不能访问
//a.y = 17; //错误,外部不能访问
a.setXY(11,17); //public可以外部访问
a.print(); //通过上一个函数,利用内部访问,打印x和y
return 0;
}
//**********************************
数据成员可以是任何数据类型,但不能用auto、register或extern说明
说明:
定义对象之后,会为对象分配存储空间
全局的对象在任何函数内均可使用,而局部对象只能在定义对象的函数中使用
这也许会使人联想到函数的重载与覆盖的区别,但稍加对比就会发现两者是完全不同的:
(1) 重载的几个函数必须在同一个类中; 覆盖的函数必须在有继承关系的不同的类中;
(2) 覆盖的函数前必须加关键字Virtual;重载和Virtual没有任何瓜葛,加不加都不影响重载的运作。
(3) 覆盖的几个函数必须函数名、参数、返回值都相同;
重载的函数必须函数名相同,参数不同。参数不同的目的就是为了在函数调用的时候编译器能够通过参数来判断程序是在调用的哪个函数。这也就很自然地解释了为什么函数不能通过返回值不同来重载,因为程序在调用函数时很有可能不关心返回值,编译器就无法从代码中看出程序在调用的是哪个函数了。
#include
class Base
{
public:
virtual void f(float x){ cout << "Base::f(float) " << x << endl; }
void g(float x){ cout << "Base::g(float) " << x << endl; }
void h(float x){ cout << "Base::h(float) " << x << endl; }
};
class Derived : public Base
{
public:
virtual void f(float x){ cout << "Derived::f(float) " << x << endl; }
void g(int x){ cout << "Derived::g(int) " << x << endl; }
void h(float x){ cout << "Derived::h(float) " << x << endl; }
};
void main(void)
{
Derived d;
Base *pb = &d;
Derived *pd = &d;
// Good : behavior depends solely on type of the object
pb->f(3.14f); // Derived::f(float) 3.14
pd->f(3.14f); // Derived::f(float) 3.14
// Bad : behavior depends on type of the pointer
pb->g(3.14f); // Base::g(float) 3.14
pd->g(3.14f); // Derived::g(int) 3 (surprise!)
// Bad : behavior depends on type of the pointer
pb->h(3.14f); // Base::h(float) 3.14 (surprise!)
pd->h(3.14f); // Derived::h(float) 3.14
} pb是基类指针,pd是派生类指针,pd的所有函数调用都只是调用自己的函数,和多态性无关,所以pd的所有函数调用的结果都输出Derived::是完全正常的;pb的函数调用如果有virtual则根据多态性调用派生类的,如果没有virtual则是正常的静态函数调用,还是调用基类的,所以有virtual的f函数调用输出Derived::,其它两个没有virtual则还是输出Base::很正常啊,nothing surprise!
记住“只有在通过基类指针或引用间接指向派生类子类型时多态性才会起作用”
包含(或继承)一个或多个纯虚拟函数的类被编译器识别为抽象基类。试图创建一个抽象基类的独立类对象会导致编译时刻错误。