0.指针
空指针:可用来初始化,不可访问其内存地址 野指针:指向了非法内存空间
32位 指针所占内存:4个字节 64位 8个字节
const int *p 常量指针 允许改变指针指向,不允许通过指针改变其指向的值。
eg:const int *p = &a;
p = &b;√
*p = 100;×
int * const p 指针常量 不允许改变指针指向,允许通过指针改变其指向的值。
值传递/地址传递
结构体中const作用:防止误操作
函数中的形参改为指针,可以节省内存空间,且不会复制新的副本出来
const 修饰结构体形参 不能修改值 只能读
引用 (本质是一个指针常量)
数据类型 &别名 = 原名
注意:引用必须初始化,一旦初始化就不能再修改
引用传递 可以代替 地址传递,此时形参可以修饰实参
引用可以作为函数返回值,此时函数调用可以作为等式左值,但不要返回局部变量引用
const 修饰引用
函数的占位参数 只写数据类型,不写变量名
修饰符
restrict | 由 restrict 修饰的指针是唯一一种访问它所指向的对象的方式 |
---|---|
1.继承与派生
一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数
派生: class 新类名 : access-specifier 基类名
访问修饰符 access-specifier 是 public、protected 或 private 其中的一个,如果未使用访问修饰符 access-specifier,则默认为 private。 我们通常使用 public 继承。
派生类可以访问基类中所有的非私有成员。因此基类成员如果不想被派生类的成员函数访问,则应在基类中声明为 private。
公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
2.运算符重载
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
eg: Box operator+(const Box&);<br />声明加法运算符用于把两个 Box 对象相加,返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员 函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数,如下所示:<br /> Box operator+(const Box&, const Box&);
3.数据抽象与数据封装
(1)数据抽象是指,只向外界提供关键信息,并隐藏其后台的实现细节,只表现必要的信息而不呈现细节。
数据抽象是一种依赖于接口和实现分离的编程技术。
数据抽象有两个重要的优势:
- 类的内部受到保护,不会因无意的用户级错误导致对象状态受损。
- 类实现可能随着时间的推移而发生变化,以便应对不断变化的需求,或者应对那些要求不改变用户级代码的错误报告。
数据抽象只是一个把实现细节与相关的数据分离开的概念,因而任何带有公有和私有成员的类都可以算是数据抽象的实例。
eg:把一些关键变量设成私有访问
(2)数据封装是一种把数据和操作数据的函数捆绑在一起的机制,数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。
C++ 通过创建类来支持封装和数据隐藏(public、protected、private)。
4.多态与接口(抽象类)
(1)C++ 多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。
多态的实现是通过使用虚函数:
我们在基类中使用关键字 virtual 声明的函数就是虚函数,在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。我们可以在程序中任意位置根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。
eg:virtual int area() {
cout << “Parent class area :” <
}
我们在子类中可以重写area函数,来对应不同子类实现特定功能。
(2)接口描述了类的行为和功能,而不需要完成类的特定实现。
C++ 接口是使用抽象类来实现的,不要混淆抽象类与数据抽象。
如果类中至少有一个函数被声明为纯虚函数,则这个类就是抽象类。纯虚函数是通过在声明中使用 “= 0” 来指定的。
如果一个 抽象类 的子类需要被实例化,则必须实现每个虚函数,这也意味着 C++ 支持使用抽象类声明接口。如果没有在派生类中重写纯虚函数,就尝试实例化该类的对象,会导致编译错误。
以下的示例中,基类 Shape 提供了一个接口 getArea(),在两个派生类 Rectangle 和 Triangle 中分别实现了 getArea():
// 基类
class Shape
{
public:
// 提供接口框架的纯虚函数
virtual int getArea() = 0;
void setWidth(int w)
{
width = w;
}
void setHeight(int h)
{
height = h;
}
protected:
int width;
int height;
};
// 两个派生类
class Rectangle: public Shape class Triangle: public Shape
{ {
public: public:
int getArea() //虚函数实现 int getArea()
{ {
return (width height); return (width height)/2;
} }
}; };
5.动态内存
double pvalue = NULL; // 初始化为 null 的指针
pvalue = new double; // 为变量请求内存
如果自由存储区已被用完,可能无法成功分配内存。所以建议检查 new 运算符是否返回 NULL 指针,并采取以下适当的操作:
double pvalue = NULL;
if( !(pvalue = new double )){
cout << “Error: out of memory.” <
}
一维数组
// 动态分配,数组长度为 m
int array=new int [m];
//释放内存
delete [] array;
二维数组
int **array /
/ 假定数组第一维长度为 m, 第二维长度为 n
// 动态分配空间
array = new int [m];
for( int i=0; i
//释放
for( int i=0; i
delete [] array;
创建对象实例同理
6.异常处理
(1)catch 块跟在 try 块后面,用于捕获异常,我们可以指定想要捕捉的异常类型,这是由 catch 关键字后的括号内的异常声明决定的。
try
{
// 保护代码
}
catch( ExceptionName e ) //写的是throw出的异常的类型
{
// 处理异常的代码
}
上面的代码会捕获一个类型为 ExceptionName 的异常。
C++ 提供了一系列标准的异常,定义在
#include
catch(std::exception& e)
{
//其他的错误
}
(2)我们可以通过继承和重载 exception 类来定义新的异常。下面的实例演示了如何使用 std::exception 类来实现自己的异常:
实例
#include
#include
using namespace std;
struct MyException : public exception
{
const char * what () const throw ()
{
return “C++ Exception”;
}
};
int main()
{
try
{
throw MyException();
}
catch(MyException& e)
{
std::cout << “MyException caught” << std::endl;
std::cout << e.what() << std::endl;
}
}
注:const throw() 不是函数,这个东西叫 异常规格说明 ,表示 what 函数可以抛出异常的类型,类型说明放到 throw之后的() 里。
这个()里面没有类型,就是声明这个函数不抛出异常。
如果干脆不写const throw,就表示函数可以抛出任何类型的异常。
7.模板
函数模板
类模板
8.命名空间
命名空间这个概念,可作为附加信息来区分不同库中相同名称的函数、类、变量等。使用了命名空间即定义了上下文。本质上,命名空间就是定义了一个范围。
命名空间的定义:
namespace 命名空间的名称 {
// 代码声明
}
补充:双冒号(::)
如果声明了一个类A,类A里声明了一个成员函数void f(),但没有在类的声明里给出f的定义,那么在类外定义f时,
就要写成void A::f(),表示这个f()函数是类A的成员函数。