想要看懂某个元素的声明符的具体类型是什么,最简单的办法就是从右往左读,离变量名最近的符号对其类型有最直接的影响
目录
- inline函数
- static关键词
- const关键字
- 指针带不带*的区别
- &与*
- C++中.和::和:和- >的区别
- 类class和结构体struct和联合体union
- 指针和引用的区别
- 指针作为形参进行调用
- 引用作为形参进行调用
- 指针的引用和指向引用的指针
- C++中protected和private的区别
- C++中操作符重载的使用
- C++中的迭代器
- C++中template(模板)的使用
- C++中函数加const 的区别
- C++中动态绑定和静态绑定
- C++实现多态
- 指针函数和函数指针
- C++中申请和释放内存空间的操作
- 关于结构体typedef struct
- (二级指针)指针的指针有什么用
- C++模板中class和typename的区别
- 指针点运算和箭头运算的区别
- vector中的remove和erase区别
- 重写(覆盖)、重载和隐藏
- C++对虚函数的规定
inline函数
缺省的意思即为系统默认状态
inline 是一种**“用于实现的关键字”,而不是一种“用于声明的关键字”**。
static关键词
C++中static的作用
c/c++共有
-
修饰全局变量时,表明一个全局变量只对定义在同一文件中的函数可见。
-
修饰局部变量时,表明该变量的值不会因为函数终止而丢失。
-
修饰函数时,表明该函数只在同一文件中调用。
c++独有:
- 修饰类的数据成员,表明对该类所有对象这个数据成员都只有一个实例。即该实例归 所有对象共有。
- 用static修饰不访问非静态数据成员的类成员函数。这意味着一个静态成员函数只能访问它的参数、类的静态数据成员和全局变量。
调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数,也可以用类名::函数名调用(因为他本来就是属于类的,用类名调用很正常)。因为静态是属于类的,它是不知道你创建了10个还是100个对象,所以它对你对象的函数或者数据是一无所知的,所以它没办法调用,而反过来,你创建的对象是对类一清二楚的(不然你怎么从它那里实例化呢),所以你是可以调用类函数和类成员的
const关键字
指针带不带*的区别
如图所示pint是一个指针,当它带星号的时候,*pint等于a的值。不带指针的时候,pint作为指针保存的是a的地址,但pint有自己的地址。
指针不带星号内容则为指向元素的地址,加上星号则是取出地址的值。
&与*
&放在typename后面叫做传入reference
&放在object前面叫做引地址
星号 *
-
声明的时候有*, 表示指针变量
int *p=&a;// '&'的作用就是把a变量在内存中的地址给提取出来
-
* +地址, 表示地址操作符
-
数字*数字, 表示乘法
-
解引用 (寻找指针所指的地址里面的内容)
*p=5; //这就是解引用,*解释了 p对常量的内存地址的引用,解释结果就是直接去寻找p所指内容,因为p指向a,所以a的内容将被修改,而不是原来的常量值。
#include <iostream>
using namespace std;
int main()
{
//1.在声明变量的时候* 指针变量
int a = 12;
int *p = &a;
*p; //2.地址操作符 读和写
cout << *p << endl; //读取
*p = 123; //写入
int b = 12;
int c = 12 * 23 * b; //3.数字*数字 表示乘法
//内存操作, 读 写 取地址&
system("pause");
return 0;
}
引用 &
1. 按位与 (二进制运算)
2. 取地址
3. 声明一个引用
引用 做函数参数
#include <iostream>
using namespace std;
//引用做参数
void fun(int& a)
{
cout << a << endl;
}
int main()
{
int b = 12;
fun(b);
system("pause");
return 0;
}
结合星号* 引用&
#include <iostream>
using namespace std;
//引用做参数
int main()
{
int i = 1024;
int *p = &i; // p指针指向i
int **p2 = &p; // p2指针指向p指针
cout << " i: " << i << " p: " << p << " p2: " << p2 << endl; // 分别打印 原始对象, 指针, 指针的指针
cout << " *p: " << *p << " **p2: " << **p2 << endl; // *p 解引用, **p2 两次解引用
system("pause");
return 0;
}
结果为
C++中.和::和:和- >的区别
- A.B A为对象或者结构体
- A->B A为指针,->代表成员提取,A->B是提取A中的成员B,A只能是指向类、结构、联合的指针;
- ::是作用域运算符,A::B表示作用域A中的名称B,A可以是命名空间、类、结构;
静态数据类型
静态成员函数
嵌套类
只有这三中类型可以通过类名::直接调用
- :表示继承
- :构造函数
1)对含有对象成员的对象进行初始化类
line有两个私有对象成员startpoint、endpoint,line的构造函数写成:
line(int sx,int sy,int ex,int ey):startpoint(sx,sy),endpoint(ex,ey){……}
初始化时按照类定义中对象成员的顺序分别调用各自对象的构造函数,再执行自己的构造函数
2)对于不含对象成员的对象,初始化时也可以套用上面的格式
例如,类rectangle有两个数据成员length、width,其构造函数写成:
rectangle():length(1),width(2){}
rectangle(int x,int y):length(x),width(y){}
3、对父类进行初始化
例如,CDlgCalcDlg的父类是MFC类CDialog,其构造函数写为:
CDlgCalcDlg(CWnd* pParent ): CDialog(CDlgCalcDlg::IDD, pParent)
其中IDD是一个枚举元素,标志对话框模板的ID
使用初始化成员列表对对象进行初始化,有时是必须的,有时是出于提高效率的考虑
类class和结构体struct和联合体union
指针和引用的区别
CSDN浏览第一的使用函数是错误的,讲解也是,妈的,前九个说的还行,作为参数传递的区别已经修改函数
- 指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;
而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。如:
int a=1;int *p=&a;
int a=1;int &b=a;
上面定义了一个整形变量a和一个指针变量p,该指针变量p指向a的存储单元,即p的值是a存储单元的地址。
而下面2句定义了一个整形变量a和这个整形a的引用b,事实上a和b是同一个东西,在内存占有同一个存储单元。
-
引用不可以为空,当被创建的时候,必须初始化,而指针可以是空值,可以在任何时候被初始化。
-
可以有const指针,但是没有const引用;
-
指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a是不合法的)
-
指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;
-
指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了,从某种意义上来讲引用可以被认为为是不可以改变的指针。
-
”sizeof引用”得到的是所指向的变量(对象)的大小,而”sizeof指针”得到的是指针本身的大小;
-
指针和引用的自增(++)运算意义不一样,指针是指向下一位,而引用则是讲引用的元素+1
-
如果返回动态内存分配的对象或者内存,必须使用指针,引用可能引起内存泄漏;
-
作为参数传递的时候,形参对实参始终是拷贝赋值的操作,可以使用指针获得传入实参的地址从而对实参进行变化,由于引用的赋值(初始化)只是相当于实参的一个别名因此也能做到对实参进行改变。
指针作为形参进行调用
1)指针
#include<iostream>
#include<stdlib.h>
using namespace std;
void swap_int(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void test(int *p)
{
cout <<"test中的p的内容="<< p <<" ,test中p指向的值="<<*p<<",test中p的地址="<<&p<<endl<<endl;
int a = 1;
p = &a;
cout<<"test更新后p的内容与指向的值" << p << " " << *p << endl<<endl ;
}
int main(void)
{
int a = 1, b = 2;
int c = 3;
int *p = &c;
swap_int(&a, &b);
cout << a << " " << b << endl << endl;
test(p);//传入p的内容即c的值
cout << " main中的p的内容=" << p << " ,main中的p指向的值=" << *p <<" ,main中的p的地址="<<&p<< endl<<endl;
if (p == &c)
cout << "指针p为c" << endl << endl;
system("pause");
}
引用作为形参进行调用
#include<iostream>
#include<stdlib.h>
using namespace std;
void test(int &a)
{
cout<<&a<<" "<<a<<endl<<endl;
}
int main(void)
{
int a=1;
cout<<&a<<" "<<a<<endl<<endl;
test(a);
system("pause");
}
结果为:
0x6afefc 1
0x6afefc 1
3)指针的引用*&
#include<iostream>
#include<stdlib.h>
using namespace std;
void test(int *&p)
{
int a=1;
p=&a;
cout<<p<<" "<<*p<<endl<<endl;
}
int main(void)
{
int *p=NULL;
test(p);
if(p!=NULL)
cout<<"指针p不为NULL"<<endl<<endl;
cout<<p<<" "<<*p<<endl<<endl;
system("pause");
}
结果为:
0x6afefc 1
指针p不为NULL
0x6afefc 1
指针的引用和指向引用的指针
C++中protected和private的区别
首先这两个都是访问类中成员权限的限制符。
public:可以被该类中的函数、子类的函数、友元函数访问,也可以由该类的对象访问;
protected:可以被该类中的函数、子类的函数、友元函数访问,但不可以由该类的对象访问;
private:可以被该类中的函数、友元函数访问,但不可以由子类的函数、该类的对象、访问。
在类外如果想使用类中的成员,只能直接使用public类型的,proteced和private都是不能访问的,对于类外而言,这两个是完全相同的。
- 访问范围
private: 只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类的对象也不能访问.
protected: 可以被该类中的函数、子类的函数、以及其友元函数访问,但不能被该类的对象访问
注:友元函数包括两种:设为友元的全局函数,设为友元类中的成员函数
- 继承后方法属性的变化
private继承,父类的所有方法在子类中变为private;
protected继承,父类的protected和public方法在子类中变为protected,private方法不变;
C++中操作符重载的使用
C++中操作符重载的使用
重载的运算符是具有特殊名字的函数:它们的名字由关键字operator和其后要定义的运算符共同组成。
**当一个重载的运算符是成员函数时,this绑定到左侧运算对象。**成员运算符函数的(显示)参数数量比运算对象的数量少一个。
通常情况下,不应该重载逗号、取地址、逻辑与和逻辑或运算符。
Time operator++();//前置递增运算符重载
Time operator++(int);//后置递增运算符重载
C++中的迭代器
C++中template(模板)的使用
C++中函数加const 的区别
C++中动态绑定和静态绑定
C++实现多态
虚函数表刨析
关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。
指向基类的指针在操作它的多态类对象时,会根据不同的类对象,调用其相应的函数,这个函数就是虚函数。
C++中的多态
指针函数和函数指针
函数指针和指针函数详解
指针函数设定的返回值是指针,函数指针就是指向函数的指针。
函数指针声明如下:
ret (*p)(args, …);
其中,ret为返回值,*p作为一个整体,代表的是指向该函数的指针,args为形参列表。其中p被称为函数指针变量 。
C++中申请和释放内存空间的操作
malloc用于申请一个空间,calloc常用于申请数组空间,realloc常用于更改空间大小。
三个申请内存的函数以及释放内存的函数
关于结构体typedef struct
若struct node{ }这样来定义结构体的话。在定义 node 的结构体变量时,需要这样写:struct node n;
若用typedef,可以这样写:typedef struct node{}NODE; 。在申请变量时就可以这样写:NODE n;其实就相当于 NODE 是node 的别名。区别就在于使用时,是否可以省去struct这个关键字。
typedef struct Node
{
Elemtype data;
struct Node* next;
}node;
此处的node是结构体类型的别名,此外此时也可以不写Node
可以通过node *t来声明一个struct Node类型的指针
struct Node
{
Elemtype data;
struct Node* next;
}node;
此处的node是一个结构体变量
可以通过node.data来查看node的data
当存在typedef的时候,放在struct之后大括号之后的变量名都是结构体的别名。
当不存在typedef的时候,则可以视为结构体变量。
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
struct LNode {
ElemType data;
struct LNode *next;
};
typedef struct LNode LNode;
typedef struct LNode* LinkList;
以上两段代码是等价的。
此时可以通过LNode声明一个struct Lnode类型的变量
可以通过LinkList声明一个struct Lnode*类型的变量
(二级指针)指针的指针有什么用
int* a;
a是一级指针,a代表理原始变量的值,a代表一个指针
int** p
p是一个指针 **p代表原始变量的值
如果p是一个二级指针,则p代表了它所指向的指针,**p代表了它指向的指针所指向的元素
void x(int* a)
{
a=&b
}
此时a是传入实参指针的拷贝,a和实参并不是同一个值
当改变a的内容为b的地址的时候,实参并没有发生改变
void x(int** p)
{
*p=&b
}
此时p指向了实参的地址,**p可以查看指向的实参指针指向的变量的值
由于p放的是实参指针的地址,因此*p代表实参,可以视为实参的别名
当*p的内容更改为b的地址的时候,实参指向的变量地址也发生了改变
指针由两部分组成(指向内容的地址+本身地址),当我们*指针的时候实际上是读取指向内容的地址来查看所指向内容
C++模板中class和typename的区别
template<class T>
void mySwap(T &a, T &b)
{
T tmp = a;
a = b;
b = tmp;
}
int a = 10;
int b = 20;
mySwap(a, b);
以上代码可以直接编译
template<typename T>
void mySwap(T &a, T &b)
{
T tmp = a;
a = b;
b = tmp;
}
int a = 10;
int b = 20;
mySwap(a, b);
编译出错,因为需要指定a和b套用了什么类型即调用改为
mySwap<int>(a,b);
以下情况不能使用class
此外typename还有一个功能
在模板函数内部的时候使用typename作为前缀,当遇见此语句的时候会提示编译器声明的是一个类型变量而不是引用模板内的静态成员。
在模板类型没有实例化之前,编译器有时候并不知道C::const_iterator是个什么东西,有三种可能
编译器默认将C::const_iterator解析为一个变量,所以我们需要告诉编译器它是一个类型。
静态数据成员
静态成员函数
嵌套类型
此时使用typename就是将这个const_iterator限定为嵌套类型
指针点运算和箭头运算的区别
其实点运算是结构体变量访问其成员的操作符
箭头运算是结构体指针访问其指向的成员变量的操作符
例如
struct song
{
int a;
}
int main{
song s;
s.a=1;
song *p;
p=&s;
}
(*)p.a 和 p->a 是等价的
PS:
1.点运算比运算优先级高,故如果用 . 运算,括号不可省。因为()p是指针指向的结构体变量,故可以用点运算来访问其成员
2.而箭头运算只能指针用,比如 声名 int *p
p是指针,*p就是p所指向的一个int 变量的内存
vector中的remove和erase区别
重写(覆盖)、重载和隐藏
区分重载(overload),覆盖(Override)和隐藏(hide) 收藏
重载overload,这个概念是大家熟知的。在同一可访问区内被声名的几个具有不同参数列的(参数的类型、个数、顺序不同)同名函数,程序会根据不同的参数列来确定具体调用哪个函数,这种机制就是重载。重载不关心函数的返回值类型,即返回类型不同无法构成重载。此外,C++ 中的const成员函数也可以构成overload。
总结一下重载的特征:
1、处在相同的空间中,即相同的范围内;
2、函数名相同;
3、参数不同,即参数个数不同,或相同位置的参数类型不同;
4、const成员函数可以和非const成员函数形成重载;
5、virtual关键字、返回类型对是否够成重载无任何影响。
覆盖override,是指派生类中存在重新定义的函数,其函数名、参数列、返回值类型必须同父类中的相对应被覆盖的函数严格一致,覆盖函数和被覆盖函数只有函数体(花括号中的部分)不同,当派生类对象调用子类中该同名函数时会自动调用子类中的覆盖版本,而不是父类中的被覆盖函数版本,这种机制就叫做覆盖,特征是:
1、不同的范围(分别位于派生类与基类);
2、函数名字相同;
3、参数相同;
4、基类函数必须有virtual关键字。
针对上述两个概念,还有一个隐藏hide。所谓的隐藏,指的是派生类类型的对象、指针、引用访问基类和派生类都有的同名函数时,访问的是派生类的函数,即隐藏了基类的同名函数。隐藏规则的底层原因其实是C++的名字解析过程。在继承机制下,派生类的类域被嵌套在基类的类域中。派生类的名字解析过程如下:
1、首先在派生类类域中查找该名字。
2、如果第一步中没有成功查找到该名字,即在派生类的类域中无法对该名字进行解析,则编译器在外围基类类域对查找该名字的定义。
总结一下隐藏的特征:
1、如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
2、如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
C++对虚函数的规定
c++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数。因此,在子类从新声明该虚函数时,可以加,也可以不加,但习惯上每一层声明函数时都加virtual,使程序更加清晰。