C++关键技术总结.docx
- 文档编号:9240502
- 上传时间:2023-02-03
- 格式:DOCX
- 页数:18
- 大小:20.89KB
C++关键技术总结.docx
《C++关键技术总结.docx》由会员分享,可在线阅读,更多相关《C++关键技术总结.docx(18页珍藏版)》请在冰豆网上搜索。
C++关键技术总结
第一章 从C到C++
1、 利用常量
C语言中利用define预定义符定义符号常量,C++中利用const关键字定义常量,其好处是定义的常量具有数据类型。
2、 内联函数
对于一些函数体代码不大,但又被频繁调用的函数,利用内联函数可以提高效率。
但是内联函数的代价是需要占用更多的空间,如果程序在10个不同的地方调用了同一个内联函数,则程序将包含该函数的10个代码的拷贝。
所以内联函数的实际是用空间来换时间——空间开销增大了,时间开销减少了。
在函数的定义处加上inline关键字说明函数为内联函数,但只是向编译器发出内联的请求,编译器在编译的时候未必会把该函数编译为内联函数。
内联函数和带参数的宏的区别:
内联函数是通过传递参数实现的,而不是通过简单的文本替换来实现的。
注意:
(1)内联函数通常不能包括复杂结构的控制语句:
如switch、while、复杂语句嵌套,否则,系统将作为普通函数处理。
(2)递归函数不能作为内联函数。
(3)内联函数适合只有1~5行的小函数
(4)在类结构中,在类体中定义的成员函数都是内联函数。
3、 函数重载
带有默认参数的函数重载常引起二义性。
如以下三个函数:
int fun(int m, int n, int j=5)
int fun(int m, int n=3, int j=5)
int fun(int m=1, int n=3, int j=5)
在调用fun(1, 3, 5)时,系统将产生二义性,因为都严格匹配上面的三个函数,所以编译器不知道要调用哪一个。
4、 函数模板
函数模板是通用的函数描述,它们使用通用类型来定义函数,其中的通用类型可以是具体的类型(如int或double)替换。
通过将类型作为参数传递给模板,可使编译器生成该类型的函数。
template
{
T tmp;
tmp = a;
a = b;
b = tmp;
}
int main()
{ int a = 10, b = 20;
float x = 3.14f, y = 8.71f;
cout << "Before swap:
" << endl;
cout << a << " " << b << endl;
cout << x << " " << y << endl;
Swap(a, b);
Swap(x, y);
cout << "After swap:
" << endl;
cout << a << " " << b << endl;
cout << x << " " << y << endl;
return 0;
}
其中AnyType一般为了简化,就定义为T。
5、 指针
(1)const和指针的组合情况
指向常量的指针 const int *p 或者 int const *p
常指针 int *const p
指向常量的常指针 const int *const p
(2)一定用配对的使用new和delete,否则会发生内存泄漏问题。
在执行完delete之后,要把指针的值赋为NULL,以避免产生野指针。
6、 引用
引用就是为某个变量起了个别名,对别名的操作就等同于对目标变量的操作。
引用本身不是变量,它是某个变量的别名,其本身不占用存储空间。
定义引用的时候必须指出目标对象。
7、 名称空间
名称空间的意义主要在于解决名字冲突问题。
namespace namespace_name
{
//各种名称(包括类型名、变量名和函数名)
}
第二章 类和对象
1、 类和对象的定义
类是具有相同属性和操作的一组对象的集合。
类的实例就是对象。
将一个对象赋值给另一个对象时,所有的数据成员都会逐个复制。
2、 友员
在C++中,友员主要有两种类型:
友员类和友员函数
(1)友员类
一个类A如果是类B的友员类,则要在类B中要声明friend class A。
这样A就是成为了B的友员类,则类A中的所有成员函数都可以访问类B中保护或者私有成员。
友员关系具备单向、不交换、不传递的特性。
单方向:
若A类为B类的友员类,并不意味着B也具有友员类A
不传递:
若A类是B类的友员类,B类是C类的友员类,并不意味着A可以直接存取C中的保护或私有变量。
(2)友员函数
友员函数不是当前类的成员函数,是独立于当前类的外部函数,它可以访问当前类的私有数据成员和公有数据成员。
3、 类的静态数据
(1)静态数据成员
静态数据成员属于类,而不像普通数据成员一样属于某一个对象,因此要利用“类名:
:
”访问静态的数据成员。
静态数据成员不能在类中初始化,一般在main()函数之前初始化,如果不初始化,默认初始化为0。
静态数据成员具有全局变量的一些特性,建议把静态数据成员说明为私有的。
(2)静态成员函数
静态成员函数利用“类名:
:
”作为它的限定词。
由于静态成员函数不属于某个对象,因此一般用静态成员函数访问静态数据成员或者全局变量。
一般而言,静态成员函数不访问类中的非静态数据成员。
若确实有需要,静态成员函数只能通过对象名访问该对象的非静态成员。
4、 this指针
this指针总是指向当前对象。
通过“this->成员名”或者“(*this).成员名”来表示当前对象中的某个成员。
5、 构造函数
构造函数的特性:
(1)构造函数的名字必须和类名一致
(2)构造函数可以带参数,但是不能有返回值
(3)定义对象时,编译系统自动调用构造函数
(4)构造函数不能像其它成员函数那样被显示地调用,它是在定义对象的同时被调用。
(5)构造函数可以有多个,如果没有定义构造函数,系统会提供一个默认的构造函数,(6)它只负责创建对象,不做任何的初始化操作。
拷贝构造函数:
通过已存在的对象创建一个新对象。
类名 (const 类名 &ob)
{ //拷贝构造函数体
}
如果没有编写自定义的拷贝构造函数,C++会自动将一个已存在的对象复制给新对象,这种按成员逐一复制的过程由默认的拷贝构造函数自动完成。
拷贝构造函数和缺省的拷贝构造函数区别在于:
拷贝构造函数用自定义的拷贝构造函数所给出的算法初始化新对象的数据成员,也就是说已知对象和新对象有区别。
而采用缺省的拷贝构造函数初始化对象,则新对象和已知对象一致。
在C++程序中,如果使用默认构造函数创建两个对象,则这两个对象指向同一个资源,这中拷贝称之为浅拷贝,若使用自定义的拷贝构造函数创建两个对象,则这两个对象指向不同的资源,这种拷贝称为深拷贝。
以下是一个深拷贝的一个例子:
#include
using namespace std;
class student
{
public:
student(char *sn)
{
cout << "constructing " << sn << endl;
sname = new char(strlen(sn) + 1);
if (sname !
= NULL)
strcpy(sname, sn);
}
student(student &s)
{
cout << "copy" << endl;
sname = new char(strlen(s.sname) + 1);
if (sname !
= NULL)
strcpy(sname, s.sname);
}
~student()
{
cout << "destructing " << sname << endl;
delete[] sname;
sname = NULL;
}
protected:
char *sname;
};
int main()
{
student p1("wang");
student p2(p1);
return 0;
}
在VC下编译能通过,但是在运行时出错了。
但是在linux环境下,g++编译和运行都正确。
我估计在VC下都当成浅拷贝了。
6、 析构函数
一个类只能有一个析构函数,没有参数,没有返回值,用户可以自己定义析构函数,也可以使用编译系统生成的默认析构函数。
析构调用过程是构造顺序的逆过程。
最先构造的对象,最后析构。
由于析构函数不能带参数,故一个类中只能有一个析构函数。
一般来说,如果一个类中定义了虚函数,析构函数也应定义为虚析构函数,其主要用来完成对象消亡之前做一些清理工作,比如释放内存等。
一般情况下类的析构函数作用是:
释放内存资源,而此析构函数不能被调用的话会造成内存泄漏。
此时将析构函数定义为虚函数就特别重要。
7、 成员初始化列表及初始化
在进行含有对象成员的类对象的初始化时间,首先按照类定义对象成员的顺序来分别调用各自的对象成员所对用的构造函数,然后再执行该类自己定义的构造函数。
8、 继承
子类继承父类之后,如果修改了父类的方法,在调用父类的方法的时候,要使用(子类的对象).父类名:
:
(方法名)来调用。
#include
using namespace std;
class Base
{
public:
void fun()
{
cout << "In Base" << endl;
}
};
class Pai:
public Base
{
public:
void fun()
{
cout << "In Pai" << endl;
}
};
int main()
{
Pai a;
a.Base:
:
fun();
a.fun();
return 0;
}
第三章 多态
1、虚函数
C++支持两种类型的多态性,一种是编译时多态,一种是运行时多态。
(1)编译时的多态性:
也称静态多态,是通过函数的重载来实现的,到底执行哪一个重载版本在编译时就知道了。
(2)运行时的多态性:
也称动态多态,是直到系统运行时,才根据实际情况决定实现何种操作。
C++中,运行时的多态性通过虚函数机制来实现。
编译时的多态性具有运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点,也就是说,到底运行哪个函数版本,需要在运行时通过找出发送消息的对象来确定,编译器在编译时采用的是动态联编的手段。
动态联编的一个例子:
#include
using namespace std;
class Shape
{
public:
virtual void Area()
{
cout << "这是基类形状类的求面积函数" << endl;
}
};
class Rectangle:
public Shape
{
public:
void Area()
{
cout << "这是派生类矩形的求面积函数" << endl;
}
};
int main()
{
Shape sh1, *pShape;
Rectangle rect1;
pShape = &sh1; //指针pShape指向Shape对象sh1
pShape->Area();
pShape = &rect1; //指针pShape指向Rectangle对象rect1
pShape->Area();
return 0;
}
2、纯虚函数
纯虚函数的一般格式为:
Virtual 返回类型 函数名(参数表)=0
(1) 定义纯虚函数时,没有实现部分,不能产生对象,不能被直接调用
(2) “=0”本质上是将指向函数体的指针定义为NULL,纯虚函数仅起提供一个与派生类相一致的接口作用,用于派生类中具有相同名字的函数的存放处。
(3) 在派生类中必须有重新定义的纯虚函数的函数体,即使再次声明该函数为纯虚函数。
3、抽象类
如果一个类中,至少有一个纯虚函数,那么称该类为抽象类。
抽象类不能实例化,它只是将许多相关的类组织起来,提供一个公共的基类,而那些被它组织在一起的具体的类由它派生出来。
#include
using namespace std;
class CMenu //抽象类
{
public:
virtual void func() = 0; //纯虚函数
};
class CFormMenu1:
public CMenu
{
public:
void func()
{
cout << "你选择的是——新建文件菜单" << endl;
}
};
class CFormMenu2:
public CMenu
{
public:
void func()
{
cout << "你选择的是——打开文件菜单" << endl;
}
};
class CFormMenu3:
public CMenu
{
public:
void func()
{
cout << "你选择的是——保存文件菜单" << endl;
}
};
int main()
{
int choice;
CMenu *f[3]; //定义一个基类的指针数组
f[0] = new CFormMenu1;
f[1] = new CFormMenu2;
f[2] = new CFormMenu3;
do
{
cout << "1:
新建文件.." << endl;
cout << "2:
打开文件.." << endl;
cout << "3:
保存文件.." << endl;
cout << "0:
退出程序.." << endl;
cin >> choice;
if (choice >=1 && choice <=3)
f[choice - 1]->func();
}while(choice !
= 0);
return 0;
}
第四章 类模板和重载
1、类模板
类模板只是模板的定义,不是一个实在的类,类模板是对象的抽象,代表一种类型的类,这些类具有相同的功能,但是数据成员类型及成员函数返回类型和形参类型不同。
实例化类模板是将一个通用的类模板生成一个具体的类,因此说,模板类是类模板的实例,代表一个具体的类。
模板类可以定义类对象,而类模板不能定义对象,因此说模板类才是实实在在的类。
#include
using namespace std;
template
class CDemo
{
public:
CDemo(T t1, T t2, T t3);
T Min();
T Max();
private:
T x, y, z;
};
template
CDemo
:
CDemo(T t1, T t2, T t3):
x(t1), y(t2), z(t3)
{
return ;
}
template
T CDemo
:
Min()
{
T min = x;
if (y < min) min = y;
if (z < min) min = z;
return min;
}
template
T CDemo
:
Max()
{
T max = x;
if (y > max) max = y;
if (z > max) max = z;
return max;
}
int main()
{
CDemo
cout << example.Max() << endl;
cout << example.Min() << endl;
return 0;
}
2、运算符重载
C++中大部分的运算符可以重载,但是如下表所示的运算符不能重载。
运算符运算符名称禁止重载的理由
?
:
三目条件运算符C++中没有定义三目运算符的语法
.成员操作符为保证成员操作符对成员访问的安全性
:
:
作用域操作符该操作符右操作数不是表达式
sizeof类型长度操作符该操作符的操作数为类型名,不是表达式
运算符的重载形式有两种:
重载为类的成员函数和重载为类的友员函数。
这两种形式都可以访问类的私有成员。
成员函数是用this指针隐式地访问类对象的某个参数,友员函数的调用必须明确列出该参数。
当运算符函数是一个成员函数时,最左边的操作数(或者只有最左边的操作数)必须是运算符类的一个类对象(或者是该对象的引用)。
如果左边的操作数必须是不同类的对象,或者是一个内部类型的对象,该运算符函数必须作为一个非成员函数(通常是友员函数)来实现。
选择友员函数重载运算符的另一个原因是使运算符具有可交换性。
例如:
给定正确的重载运算符定义,运算符左边的参数可以是其它数据成员的对象。
特别需要注意的是:
当运算符重载为类的成员函数时,函数的参数个数比原来的操作数的个数要少一个(“++”, “——”除外);当重载为类的友员函数时,参数个数和原来的操作数的个数相同。
3、异常处理
#include
using namespace std;
class Base
{
public:
void show()
{
cout << "Base object" << endl;
}
};
class Derived:
public Base
{
public:
void show()
{
cout << "Derived object" << endl;
}
};
int main()
{
int no;
cout << "Input a integer please" << endl;
if (cin >> no)
{
try
{
if (no % 2 == 0)
throw Base(); //抛出基类对象
else
throw Derived(); //抛出派生类对象
}
catch(Derived d)
{
cout << "Exception:
";
d.show();
}
catch(Base b)
{
cout << "Exception:
";
b.show();
}
}
return 0;
}
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C+ 关键技术 总结
![提示](https://static.bdocx.com/images/bang_tan.gif)