c++总结

2018-03-23  本文已影响0人  i_Eloise


不同的C++存储方式是通过存储持续性、作用域和链接性来描述的。

C++使用三种(C++11种是4种)不同的方案来存储数据,这些方案的区别是数据保留在内存中的时间:

作用域和链接

作用域:描述名称在文件(单元)的多大范围可见
链接性:描述了名称如何在不同单元中共享
链接性在外部的名称可在文件间共享
链接性为内部的名称只能由一个文件中的函数共享
自动变量没有链接性,因为它们不能共享


存储描述 持续性 作用域 链接性 如何声明
自动 自动 代码块 无链接性 在代码块中
存储器 自动 代码块 无链接性 在代码块中,使用关键字register
静态,无链接性 静态 代码块 无链接性 在代码块中,使用关键字static
静态,外部链接性 静态 文件 外部链接性 不在任何函数内
静态,内部链接性 静态 文件 内部连接性 不在任何函数内,使用关键字static

由于静态变量的数目在程序运行期间是不变的,因此程序不需要使用特殊的装置(如栈)来管理它们。编译器将分配固定的内存块来存储所有的静态变量。

静态持续性,外部链接性
链接性为外部的变量通常简称为外部变量,外部变量也称全局变量
单定义规则:
在每个使用外部变量的文件中,都必须声明它,
定义声明:defining declaration 简称定义
引用声明,reference declaration 简称声明
引用声明使用关键字 extern

//file01.cpp
extern int cats = 20;
int dogs = 22;
int fleas;

//file02.cpp
extern int cats;
extern int dogs;

//file03.cpp
extern int cats;
extern int dogs;
extern int fleas;

作用域解释符(::),放在变量名前面时,该运算符表示使用变量的全局版本。
可以使用外部变量在多文件程序的不同部分之间共享数据;可以使用链接性为内部的静态变量在同一个文件中的多个函数之间共享数据(名称空间提供了另外一种数据共享的方法)。如果将作用域为整个文件的变量变为静态的,就不必担心其名称与其他文件中的作用域为整个文件的变量发生冲突


struct data
{
  char name[30];
  mutable int accesses;
}
const data veep={"Claybourne Clodde",0,...};
strcpy(veep.name,"Joye Joux");//not allowed
veep.accesses++;

在C++中,const对默认存储类型稍有影响。在默认情况下全局变量的链接性为外部。但const全局变量的连接性为内部。也就是说,在C++看来,全局const定义就像使用了static说明符一样

函数和链接性

所用函数的存储持续性都是静态的,即在整个程序执行期间都一直存在。默认情况下,函数的链接性为外部,即可以在文件间共享,可以在函数原型中使用关键字extern来指出函数是在另一个文件中定义的(但这是可选的)
还可以使用关键字static将函数的链接性设置为内部的,使之只能在一个文件中使用,必须同时在原型和函数定义中使用关键字static

语言链接性(c++ language linkage)

链接程序要求每个不同的函数都有不同的符号名,在C语言中,一个名称只对应一个函数。但是C++中,一个名称可能对应多一个函数。必须将这些函数翻译为不同的符号名称,因此C++编译器执行名称矫正或名称修饰,为重载函数生成不同的符号名称。链接程序在寻找于C++函数调用的匹配的函数时,使用的方法与C语言不同,但如果要在C++程序中使用C库预编译的函数,可以用函数原型来指出要使用的约定:

extern "C" void spiff(int); //use C protocal for name look-up
extern  void spoff(int);//use C++ protocal for name look-up
extern "C++" void spaff(int);//use C++ protocal for name look-up

存储方案和动态分配

前面的几种内存分配方案不使用与C++运算符new(或C函数malloc)分配的内存,这种内存被称为动态内存,动态内存由new和delete控制,而不是由作用域和链接性规则控制。与自动内存不同,动态内存不是LIFO,其分配和释放顺序要取决于new和delete在何时以何种方式被使用。
编译器使用三块独立的内存:一块用于静态变量,一块用于自动变量,一块用于动态存储。
存储方案不适用于动态内存,但使用与用来跟踪动态内存的自动和静态指针变量。
使用new运算符初始化:
int *pi = new int (6);
double * pd = new double(99.99);
struct where{double x;double y;double z;};
where *one = new where{2.5,5.3,7.2};
int * ar = new int[4]{2,3,6,7};
placement new

#include <new>
p1 = new chaff;//new a structure in heap
p1= new (buffer) chaff//new a structrure in the buffer

名称空间

声明区域(declaration region)可以在其中进行声明的区域
潜在作用域:从声明点开始,到其声明区域的结尾。
名称空间可以是全局的,也可以位于另一个名称空间中。但不能位于代码块中。在默认情况下,在名称空间中声明的名称的链接性为外部的(除非它引用了常量)
using声明 指定特定的标识符可用
using Jill::fetch;

main中的using声明Jill::fetch将fetch添加到main()定义的声明区域中。
在函数的外面使用using声明时,将把名称添加到全局名称空间中。

using编译指令 使整个名称空间可用
using namespace xxx ;


sizeof()
.
::
?:
typeid
const_cast dynamic_cast reinterpret_cast static_cast

5 表中大多数运算符都可以通过成员函数或非成员函数进行重载:但下面的运算符只能通过成员函数进行重载:

=
()
[]
->

Time Time::operator*(double mult) const
{
}

Stonewt mycat;
mycat = 19.6;//not valid if Stonewt(double) is declared as explicit
mycat = Stonewt(19.6);//ok, an explicit conversion
operator typeName();

转换函数必须是类方法,转换函数不能指定返回类型,转换函数不能有参数,但是有返回值
operator double();
可以将转换运算符声明为显示的:
···
explicit operator int() const;
explicit operator double() const;
···


友元

friend Time operator*(double m, const Time & t);
//在定义中不要使用friend

ostream & operator<<(ostream & os, const Time & t)
{
  os << t.hours << " hours" << t.minutes << "minutes";
  return os;
}
cout << trip;
ofstream fout;
fout.open("savetime.txt");
Time trip(12,40);
fout << trip;  //类的继承属性让ostream引用能指向ostream和ofstream对象

友元类
友元成员函数


特殊成员函数

默认构造函数,如果没有定义构造函数
如果定义了构造函数,C++将不会定义默认构造函数,如果希望在创建对象时不显示地对它进行初始化,则必须显示地定义默认构造函数,这种构造函数没有任何参数
带参数的构造函数也可以时默认构造函数,只要所有参数都有默认的值
默认析构函数,如果没有定义
复制构造函数,如果没有定义
新建一个对象并将它初始化为同类现有对象时,复制构造函数都将被调用

StringBad ditto(motto);
StringBad metto = motto;//
StringBad also = StringBad(motto);
//直接使用赋值构造函数生成一个临时对象,然后将临时对象的内容赋给metoo和also
StringBad* pStringBad = new StringBad(motto);
//初始化一个匿名对象,然后把新对象的地址赋值给pstring指针

定义一个显式复制构造函数以解决问题:
进行深度复制(复制构造函数应当复制字符串并将副本的地址赋给str成员)
默认构造函数逐个复制非静态成员(成员复制也称为浅复制),复制的是成员的值。
赋值运算符,如果没有定义
解决办法:进行深度复制

String::String(const String & st)
{
  num_strings++;
  len =  st.len;  
  str = new char[len+1];
  std::strcpy(str,st.str);
}
String & String::operator=(const String & st)
{
  if(this == &st)
    return *this;
  delete [] str;
  len =  st.len;  
  str = new char[len+1];
  std::strcpy(str,st.str);
  return *this;
}

地址运算符,如果没有定义
(c++11移动构造函数
移动赋值运算符)

可以重载赋值运算符,使之能接收对象的银行和直接使用常规字符串。

成员初始化列表


继承 --3种继承关系(公有继承,保护继承,私有继承)

公有继承 is-a关系 (is-a-kind-of)
class RatedPlayer : public TableTennisPlayer
{
  ...
}

公有派生,派生类对象包含积累对象。
使用公有派生,基类的公有成员将称为派生类的公有成员;基类的私有部分也将称为派生类的一部分,但只能通过基类的公有和保护方法访问。
派生类需要自己的构造函数
派生类可以根据需要添加额外的数据成员和成员函数。
派生类构造函数必须使用基类构造函数

RatedPlayer::RatedPlayer(unsigned int r,const string & fn,
const string & ln, book ht) : TableTennisPlayer(fn, ln, ht)
{
  rating = r;
}

多态公有继承

希望同一个方法在派生类和基类中的行为是不同的。方法的行为取决于调用该方法的对象。这种较复杂的行为称为多态--具有多种形态
有两种机制可以实现多态的公有继承:
在派生类中重新定义基类的方法

//ViewAcct()不是虚函数
Brass dom("Dominic Banker",11224,4183.45);
BrassPlus dot("Dorothy Banker",12118,2592.00);
Brass & b1_ref = dom;
Brass & b2_ref = dot;
b1_ref.ViewAcct();//调用Brass的
b2_ref.ViewAcct();//调用Brass的
//使用虚函数
Brass dom("Dominic Banker",11224,4183.45);
BrassPlus dot("Dorothy Banker",12118,2592.00);
Brass & b1_ref = dom;
Brass & b2_ref = dot;
b1_ref.ViewAcct();//调用Brass的
b2_ref.ViewAcct();//调用BrassPlus

1 方法在基类声明为虚后,它在派生类中将自动成为虚方法。然而,在派生类声明中使用关键字virtual来指出哪些函数是虚函数也不失为一个好方法。
2 基类声明一个虚析构函数,这样做确保释放派生类对象时,按照正确的顺序调用析构函数。如果虚构函数不是虚的,则只调用对应于指针类型的析构函数。

Brass * p_clients[CLIENTS];

将派生类引用或指针转换为基类引用或指针被称为向上强制转换,这使公有继承不需要进行显示类型转换。
编译器对非虚方法使用静态联编
对虚方法使用动态联编

overload(重载)相同函数名 不同参数
overwrite子类重写父类方法
在派生类中重新定义函数,将不是使用相同的函数特征标覆盖基类声明,而是隐藏同名的基类方法,不管参数特征标如何。

protected

派生类的成员可以直接访问基类的保护成员,但不能直接访问基类的私有成员

抽象基类

abstract base class, ABC
C++通过使用春虚函数提供未实现的函数。纯虚函数声明的结尾处为 =0
当类声明中包含纯虚函数时,则不能创建该类的对象。包含纯虚函数的类只用作基类。纯虚函数也可以有定义。

可以将ABC看作是一种必须实施的接口,ABC要求具体派生类覆盖其纯虚函数--迫使派生类遵循ABC设置的接口规则。

保护继承
私有继承

堆(heap)


指针

typedef const double (p_fun)(const double ,int);
p_fun p1 = f1; p1指向f1
p_fun pa[3] = {f1,f2,f3}; pa是一个array of 3 function pointers
p_fun (
pd)[3] = &pa; pd points to an array of 3 function pointers.


4种类型转换

C++还引入了4个强制类型转换运算符


函数

上一篇下一篇

猜你喜欢

热点阅读