C++中共有四种存储类别标识符:auto/static/register/extern
1.auto
函数或分程序内定义的变量(包括形参)可以定义为auto(自动变量)。如果不指定存储类别,则隐式定义为auto。
例如,函数类有如下定义:
auto int x , y ;
等价于:
int x , y ;
2.static
除了形参,可以将局部变量和全局变量定义为静态变量。用static标识符。
static int a;//a是全局静态变量
f()
{static int b = 1;}//b是局部静态变量
与auto不同,static变量的生存期是程序开始运行到运行结束(auto变量生存期是函数调用结束)。
若定义静态变量时没有对其赋初值,系统自动赋初值0;若赋初值,则仅在编译时赋初值一次,程序运行后不再给变量赋初值。当上一次调用局部静态变量所在的函数时,该变量的值继续有效(为上次函数调用结束时保留的值)。
3.extern
在函数外定义的变量如果没有用static声明,则是extern外部变量。外部变量只能隐式定义为extern,不能显示定义。
对外部变量声明时,系统不分配存储空间。
#include <iostream> using namespace std; int x = 2 , y = 2 ;//定义外部变量 void f1() { extern char c1 , c2 ;//声明外部变量 cin>>c1>>c2; } char c1 , c2 ;//定义外部变量 int main() { int m , n ; f1(); cout <<c1<<"+"<<c2<<"="<<x+y<< endl; return 0; }
结果是这样:
注意,如果把extern去掉了,再输入xy,回车,会出现:
因为c1,c2的作用域只在f1函数里面。如果不用extern,想达到第一幅的效果,只要把
char c1 , c2 ;//定义外部变量
这句拿到f1()前面就行了。
4.register
寄存器变量的值保存在CPU的寄存器中,读写速度更快。随着计算机硬件性能提高,现在寄存器变量使用得比较少了。
------------------------------------------------
指针和引用:
指针 = *指针变量
&取地址运算符,返回其指向的变量或数组元素的地址。
&取地址
*取目标变量
这两个运算符互为逆运算,如图(Page129,《C++程序设计》,姚琳,人民邮电出版社):
赋值运算:
注意,
可以把一个已经初始化的指针值非给另一指针,例如:
float x, *p = &x, *q = p;
可以把*理解成跟float连在一起。
这样,q和p有相同的地址,指向x。
引用(<数据类型名> &引用名=变量名;):
补充:改错题:
void test(char * OldName){ char[30] NewName; int len=0; for(;OldName[len] != '';len++){ NewName[len] = OldName[len]; } return; }
两个错误,
1.C语言中的数组定义跟Java还是有区别的,不能像上面那样定义,而要写成char NewName[30];而且NewName后面不能有空格;
2.OldName的长度需要设定一下,不然超过30之后,NewName没有那么多元素,出现溢出错误。
Mr.Yin:对于其中指针的理解,其实char NewName[30]; 比如NewName[0]就是下图的0x01元素,那么NewName就是0x00ff,也就是一个地址。
0x01 |
0x00ff
同理,有:
p[i] = *(p+i)
-----------------2016年1月26日 带成瑞------------------------------------------------------
&p[i] = p+i
类的定义:
跟Java不同,C++中类定义结束之后,要加分号。
class Student { public : void printStudent(); private: int id; char* name ; char sex; };
其中,成员函数printStudent()以函数声明的形式出现。成员函数的具体实现可以放在类定义的外部:
void Student::printStudent() { cout<<"id:"<<id<<","<<"name:"<<name<<","<<"sex:"<<sex<<endl; }
成员函数的定义和一般函数的定义区别在于「类名」和「::」构成作用域,表示出该成员函数属于的类。
一般来说,类的成员函数都放在类外进行。
一个基本原则:尽量将成员数据和成员函数设计成私有访问控制属性,体现类的「封装性」。
类的访问属性private和public的访问规则:
静态成员和友元:
静态成员数据是一个类的所有对象共享的数据成员,静态成员数据与一般数据的区别在于:对于静态成员数据,该类的每个对象都共享唯一的数据,即它只存在一份拷贝,而对于一般成员数据,该类的每个对象都独立简历自己的一个副本,以保存各自特定的值。因此,静态成员数据属于类级别的成员,而一般成员数据属于对象级别的成员。
初始化静态成员数据必须在类外进行。
比如:
class Student { public : void printStudent(); private: int id; char* name ; char sex; };
其中,成员函数printStudent()以函数声明的形式出现。成员函数的具体实现可以放在类定义的外部:
void Student::printStudent() { cout<<"id:"<<id<<","<<"name:"<<name<<","<<"sex:"<<sex<<endl; }
成员函数的定义和一般函数的定义区别在于「类名」和「::」构成作用域,表示出该成员函数属于的类。
一般来说,类的成员函数的定义都放在类外进行。
一个基本原则:尽量将成员数据和成员函数设计成私有访问控制属性,体现类的「封装性」。
class Student { public: ... private: static int count;//静态成员数据的定义 } int Student::count=0;//静态成员数据的初始化
静态成员函数是一个类所有对象共享的成员函数。静态成员函数也成为类成员函数(因为静态成员函数和静态成员数据属于类层次,而一般成员函数和一般成员数据属于对象层次。)
没有对象生成时,静态成员函数中是不能访问一般成员函数和一般成员数据的。
静态成员函数放在类内声明,类外初始化(事实上一般来说,成员函数的初始化都放在类外进行)。
友元:
静态成员数据提供了在一个类的所有对象之间共享数据的机制。
而友元则是不同类的成员函数之间、类的成员函数和一般函数之间进行数据共享的机制,破坏了封装性和数据隐藏性。不提倡使用。友元可以定义在函数级别或是类级别。
常对象(类名 const 对象名;):
对于基本数据类型的变量,可以定义符号常量,其值在程序运行过程中保持不变。
对于「类」这种自定义数据类型,同样可以定义一种对象,使得数据成员保持不变,也就是常对象。
常成员:
(1)常成员数据:可以通过定义常成员数据使得部分成员数据的值保持不变。
const 数据类型 变量名;
一般符号常量的定义和初始化是在同一语句中实现的,而常成员数据的初始化通常是分开进行的,在类的初始化列表处进行。
class Student { public: Student(int pId , Char*pName , int pAge); private: const int id; char* name; int age; };
Student::Student(int pId, char* pName, int pAge):id(pId)//这个构造函数后面的id(pId也可以理解为id变量的构造函数,在这里初始化。----Mr.Yin)
(2)常成员函数:
数据类型 函数名(形参列表) const;
1.常成员函数不能调用一般成员函数。
2.const可作为重载条件。如void printAdmin()和void printAdmin() const构成一对重载函数。
3.常对象只能定义常成员函数,不能访问一般函数。
继承与派生:
定义派生类的一般方式:
class 派生类名:继承方式 基类1,继承方式 基类2,..继承方式 基类n
{
//派生类新增的成员声明
};
例如:设已有基类base1和base2,定义派生类deriver:
class deriver: public base1 , private base2
{
private:
int m_derdate;
public:
void derfun();
}
以上定义的派生类deriver以共有方式继承基类base1,以私有方式继承基类base2,并且新增加了两个成员。
派生类自己增加的成员,完成两个需求:修改基类成员和描述新的特征和方法。
派生类修改基类的成员,通过在派生类中声明一个与基类成员同名的新成员来实现(同Java中的override)。
继承的访问控制(拍书狂魔):
主要看下划线和最下面的表格:
看public和protected:
区别在第三点:派生类以外的其他函数不可以通过派生类的对象访问从基类继承的公有成员。我理解的意思也就是,派生类不能作为一个媒介,让其他函数通过这个派生类来调用基类中的成员。
多重继承的二义性和虚基类
基类base的成员,先被继承到派生类Fderiver1,Fderiver2,然后又要被继承到派生类Sderiver,则爱生类Sderiver中有base的成员的两份拷贝。出现ambiguous错误,也就是二义性。
为解决这个问题,可以将共同基类设置成虚基类,这样在创建派生类对象时,虚基类的构造函数只调用一次,虚基类的成员在第三层派生对象中只有一份拷贝。
上例中,首先将base声明为虚基类:
class Fderiver1:virtual public base { //..原来的代码 }; class Fderiver2:virtual public base { //..原来的代码 };
其次,对第二级派生类Sderiver的构造函数作修改:
Sderiver(int attrib,int data):base(data),Fderiver1(value,data),Fderiver2(number,data) { //... }
(按:上面的构造函数后面的冒号:base(data)的用法是初始化,初始化跟赋值不同,初始化在分配内存的时候直接赋值,是一步操作;赋值是两步操作。)
编译系统只执行其中的base(data)调用对虚基类构造函数的调用,而忽略通过Fderiver1(value,data)和Fderiver2(number,data)对虚基类构造函数的调用。
--------------------------------------------
多态性与虚函数
多态性是指相同的函数名或者运算符在不同场合下使用,能表现出不同的行为或特征。
标准模板库
标准模板库(Standard Template Library,STL)主要内容包括容器、算法和迭代器。
容器有7种,分别是:向量,栈,列表,双端队列,集合,多重集合,映射。
向量:大小可变,可以像数组一样直接访问(at得到编号位置的数据),也可以像链表一样顺序访问。
一维向量的数据存取:
#include<iostream> #include<vector> using namespace std; int main() { int i = 0 ; vector<int> v ;//定义元素为整形的一维变量 for(i = 0 ; i <10 ; i++) { v.push_back(i);//push_back在容器的最后添加一个数据 } for(i = 0 ; i<v.size() ; i ++) { cout<<v[i]<<" "; } cout<<endl; return 0 ; }
程序运行结果:
0 1 2 3 4 5 6 7 8 9
其中,上面的
for(i = 0 ; i<v.size() ; i ++) { cout<<v[i]<<" "; }
用迭代器实现:
vector<int>::iterator iter; for(iter = v.begin; iter!=v.end(); iter++)//begin获取容器头指针,end获取尾指针+1 { cout<<*iter<<" "; }