目录
1 一个简单的C++程序
(1) 安装g++编译器
sudo apt-get install build-essential
g++ -v
2 函数重载
2.1 重载的概念
出现在相同作用域中的函数,有相同的函数名,而形参列表不同,称为重载函数。
int max(int a,int b);
double max(double a, double b);
char* max(char *a,char *b);
编译器根据所传递的实参类型类判断调用哪个函数。
函数不能仅仅基于不同的返回值类型来实现重载。
int f(int);
float f(int);
double f(int);
2.2 函数重载与重复声明的区别
两个函数的返回类型和形参表完全匹配,则第二个函数声明称为第一个的重复声明。
有些看起来不相同的形参表其本质是相同的。
example:判断下列函数是重载函数还是重复声明:
(1) int lookup(const int a);
int lookup(const int);
(2) typedef int INT;
int lookup(int a);
int lookup(INT a);
(3) int lookup(int a);
int lookup(const int a);
(4) int lookup(int *p);
int lookup(const int *p);
(1)重复声明
(2)重复声明
(3)重复声明
(4)重载函数
重载函数主要是通过判断是否可以对传入的参数进行修改,若是,则是重载,否则为重复。
3 引用的概念
int a = 10;
int &b = a;
b 称为a的引用,b是a的别名。
引用变量b与变量a绑定在一起,也叫相关联。
&标识引用声明符,不是取地址符。
b和a占统一内存存储单元。
(1)在定义一个引用时,必须同时初始化。
(2)当引用初始化后,只要该引用存在,它就与初始化时指向的对象相关联,不能将引用关联到其它对象。
(3)引用不是独立的数据类型,是符合类型,必须使其代表某一类型的对象。
(4)可以定义数组引用。
int a = 1024, b = 1024;
int &r1 = a;
int &r1 = b; //错误,已经定义的引用不能关联其他对象
int &r2 ; //错误,引用定义时必须初始化,未指定对象
int *p = &a;
int &r3 = &a;//错误引用不是指针
int array[10] = {0};
int (&ref)[10] = array; //正确,可以定义数组引用
int *p1 = &r1; //正确,引用是对象的别名
4 引用和指针的区别
(1)从内存分配上看:指针是一个实体,而引用仅是个别名。程序为指针变量分配内存区域,而引用不需要分配内存区域,它与它所绑定的对象占同一内存存储单元。
(2)“sizeof引用”得到的是引用所绑定的对象的大小,而“sizeof指针”得到的是指针本身的大小。
(3)引用使用时无需解引用(*), 指针需要解引用。
(4)引用只能在定义时被初始化,之后就不能改变它和所绑定的对象之间的联系,指针可改变指向。
5 引用作为函数参数
5.1 引用与非引用类型作为函数参数的区别
要求写一个函数,函数内交换两个参数:
(1)void swap(int a,int b),能否实现实参的交换?
(2)void swap(int *a,int *b),能否实现指针所指向对象的交换?
(3)void swap(int &a,int &b),能否实现实现的交换?
1)形参具有非引用类型,不论是内置类型还是指针类型,形参复制实参的值。
2)形参为指针类型,形参复制的是指针所指向对象的地址,所以函数内部可以通过对指针的使用,来修改指针所指向对象的值。
3)形参为引用类型时,它还是实参的别名,并不是实参的副本。
4)调用函数时,引用形参被创建,并且与相应的实参相关联,对形参的任何修改其实就是对实参的修改。
5)在C++中,建议多使用引用作为函数参数,比指针更方便。
5.2 使用引用形参的情况
以下两种情况优先选择引用形参:
(1)需要在函数中改变实参的值时。
(2)大型的对象(如类对象)作为实参传递时,此时复制对象所需要的时间和空间的代价都较大。
以下一种情况必须使用引用形参:
(1)对象无法进行复制时(比如IO流对象)
6 引用作为函数返回值
6.1 引用与非引用作为函数返回值的区别
函数的返回值是用于初始化在函数调用处创建的临时对象。
int func(int a)
{
int b = a + 10;
return b;
}
int main()
{
int c = func(10); //int temp = b;
} //int c = temp;
如果返回值是非引用类型,在调用函数结束后会将函数的返回值复制给临时对象,临时对象是返回值的副本。
6.2 引用作为函数返回值需要注意的问题
函数返回引用类型时,不会复制返回值,返回的是对象本身。
不要返回绑定局部对象的引用,函数调用结束,局部对象被释放,返回的引用就会指向不确定的内存。
不要返回指向栈内存的指针,因为函数调用结束,局部对象被释放,返回的指针变成了指向不再存在的悬垂指针。
6.3 课堂练习
1-6 下面的程序是否合法?如果返回值不是引用类型呢?
int& get(int *array, int index)
{
return array[index];
}
int main()
{
int a[10];
for(int i=0; i < 10; i++)
{
get(a,i) = 0;
}
}
内容回顾:
(1)引用和指针的区别?
a 引用不需要占用额外的空间,指针需要
b 引用不用解引用
c 引用必须初始化,且不能指向其它位置
(2)可以定义引用数组吗?可以
(3)什么情况下需要使用引用形参?
一般是在大型程序、发生交换
(4)函数返回值为引用类型时,需要注意哪些问题?
a 不能返回临时变量,不能指向一个栈空间
7 面向对象编程
7.1 面向过程和面向对象编程的区别
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。
(以数据为中心)分析出解决问题需要的步骤,每个步骤分别用函数来实现。
面向对象是把构成问题事物分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。
(以行为为中心)以功能来划分
7.2 五子棋为例
7.3 面向对象编程的特点
优点:
- 结构清晰
- 封装性
- 容易扩展
缺点:
- 增加工作量
- 由于面向更高的逻辑抽象层,性能上有所损失
7.4 面向对象的三个基本特征
1)封装性:private, protected, public
2)继承性:
3)多态性:
8 类的定义
8.1 类的访问权限
访问权限限定符声明类成员的访问属性:
private:私有成员: 只能被本类中的成员函数引用,类外不能调用。
public:公有成员: 可以被本类中的成员函数引用,也可以在类外被引用。
protected:受保护的成员
可以被本类的成员函数引用,不能再类外引用,可以被派生类的成员函数访问。
8.2 类对象
类是对象的抽象;对象是类的具体表现形式。
类的定义不会引起数据的内存分配。
只有定义该类的对象时才会给这个对象分配内存空间。
类对象所占的存储空间既它的数据成员所占的空间。
每个类对象都有自己的数据成员。
定义类类型的对象的方式:
Student s1,s2; 每个类对象拥有自己的数据成员,但类的成员函数只有一份
s1.age = 10;
s2.age = 20; 每个对象所占用的存储空间只是其数据成员所占用的空间,不包括成员函数s1.get_age(); 代码所占用的空间
s2.get_age(); 通过隐含的this指针来判断是哪个对象调用的成员函数
8.3 类的作用域
(1)每个类都定义了自己的作用域,在类的定义体内声明类成员,将类成员引入类的作用域。
(2)在类的作用域外,只能通过对象或者指针分别使用成员访问操作符或者->来访问。
(3)在类外定义成员函数要使用类名加作用域限定符::
(4)成员函数的形参表和函数体处于类的作用域中。
(5)成员函数的返回值类型不处于类的作用域中,如果要使用在类中定义的类型则必须在返回值前面加类名和作用域限定符。
8.4 类的定义要点
(1)类定义了一个新的类型和一个新的作用域。
(2)所有的成员必须在类的内部声明。
(3)类内可以用typedef定义类型别名。
(4)可以在类外定义成员函数,但类内必须声明。
(5)类的成员函数可以被重载。
9 类的声明
9.1 何时要进行类的声明
可以声明一个类,表示说明student是一个类,但不知道它包含哪些成员。
class student;
(1)类只声明不定义,则不能定义该类的对象。
student s1; 错误
(2)类的声明一般用于定义指向该类型的指针和引用。
student *ps = NULL;
(3)用于使用该类型作为函数形参类型或者返回值类型。
student& func1(void);
void func2(student& stu1, student& stu2);
(4)用于编写具有依赖关系的类。
9.2 课堂练习
1-9 定义两个类A和类B,类A中有一个指向类B对象的指针,类B中有一个类A的对象。
分析类A和类B的定义顺序。并验证程序。
#include <iostream>
using namespace std;
class B;
class A
{
private:
B *pb; //从上往下编译
};
class B
{
private:
A a;
};
int main()
{
A a;
B b;
}
10 类和结构体的区别
C++中类和结构体有两点不同:
(1)class默认的成员属性是private,struct默认属性是public。
(2)在继承层面:class默认的继承方式是私有继承,而struct默认的继承方式是公有继承。
11 内联函数
11.1 引入内联函数的意义
内联函数是为了解决C++预处理宏存在的问题所提出一种解决方案,用来提高程序的运行效率。
- 宏不能访问对象的私有成员
- 宏的定义很容易产生二义性
在调用内联函数时,并不真正执行函数的调用过程,只是把函数的代码嵌入到程序的调用点,减少了函数调用的开销。
11.2 内联函数要点总结
1)内联函数使用inline关键字定义
2)关键字和函数定义必须结合在一起,否则编译器将它作为普通函数对待
3)在类内部定义的成员函数自动视为内联函数,不需要加关键字Inline.
4)内联机制适用于优化小的,只有几行,且频繁调用的函数(如获取或者设置类数据成员的函数)
5)类的普通成员函数一般在源文件内定义,类的内联函数最好在头文件中定义。
内联函数是在预编译期展开。
编写完毕进入编译阶段 预编译 生产.o文件 生成.exe文件
| |
| 这里进行文件连接
内联函数嵌入发生在此阶段
一般来说:内联函数被写在头文件里面,原因:内联函数的嵌入发生在预编译阶段,程序文件还未链接
#include <iostream>
using namespace std;
#ifndef __INLINE__
#define __INLINE__
class A
{
private:
int m_data;
public:
int get_data();
void set_data(int data);
};
#endif
==============================================
#include <iostream>
#include "inline.h"
using namespace std;
int A::get_dat()
{
return m_data;
}
void A::set_data(int data)
{
m_data = data;
}
=============================================
main.cpp
#include <iostream>
#include "inline.h"
using namespace std;
int main()
{
A a;
a.set_data(10);
int b = a.get_data();
}
12 this指针
12.1 this指针的概念
每个成员函数都有一个隐含的形参this。
this是一个指针,指向调用成员函数的类对象。
在调用成员函数时,this被初始化为调用函数类对象的地址。
student s1;
s1.get_age(); s1.get_age(student* this);
student *this = &s1;
12.2 this指针的使用
1)在成员函数中,不需要显式的使用this指针来访问对象的成员,默认其成员通过this实现的引用
int student::get_age()
{
return age; //return this->age
}
当需要将一个类对象作为整体引用时,需要显式的使用this。
student& student::get_object()
{
return *this;
}
内容回顾:
- 面向对象编程的三个基本特征是什么?
- 答:封装、继承、多态
- 内联函数的原理?为什么要使用它
- 答:预编译是直接插入代码,因为快
- C++中class和struct的区别
- 答:class中默认私有,struct默认公有
- 定义类引起内存分配吗?
- 答:不引起