1.引用
(1) 声明引用时,必须同时对其进行初始化。且不能给引用本身重新赋值,使他指向另一个变量,因此引用总是const的。即没有:const double const& a=1;只有const double& a=1;
(2) 在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率
(3) 引用作为函数返回值:
1. 不能返回局部变量的引用。
2. 不能返回函数内部new分配的内存的引用
3. 引用与一些操作符的重载:对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。
(4) 引用和指针的区别:
●指针是一个实体,而引用仅是个别名;
●引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终”,指针可以“见异思迁”;
●引用没有const,指针有const,const的指针不可变;
●引用不能为空,指针可以为空;
●“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
●指针和引用的自增(++)运算意义不一样。比如:对引用变量int &ref=i重新赋值”ref=j”,并不会改变ref的指向,它仍然指向的是i,而不是j。理所当然,这时对ref进行++操作不会影响到j。而这些换做是指针的话,情况大不相同
●引用是类型安全的,而指针不是 (引用比指针多了类型检查)
2.函数实现两个数的交换
方法一:
void swap1(int *p, int *q) {
int temp;
temp= *p;
*p= *q;
*q= temp;
}
// 调用:swap1(&a, &b);
方法二:
void swap2(int &p, int &q) {
int temp;
temp= p;
p= q;
q= temp;
}
// 调用:swap2(a, b);
3.传递动态内存
1.错误示例:
#include<iostream>
void GetMemory(char* p, int num) {
p = (char*)malloc(sizeof(char)*num);
//此时的p只是函数栈中的临时副本
}
int main() {
char* str = NULL;
GetMemory(str, 100);
strcpy(str, "hello");//此时str仍然为NULL
return 0;
}
正确示例1:使用返回值
#include<iostream>
char* GetMemory(char*p, int num) {
p = (char*)malloc(sizeof(char)*num);
return p;
}
int main() {
char *str = NULL;
str = GetMemory(str, 100);
strcpy(str, "hello");
cout << *str << endl; //输出首字符, ‘h’
cout << str << endl; //输出字符串,“hello”
cout << &str << endl; //输出字符串地址
}
正确示例2:使用指针的指针
#include<iostream>
void GetMemory(char**p, int num) {
*p = (char*)malloc(sizeof(char)*num);
}
int main() {
char *str=NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
cout << *str << endl;
cout << str << endl;
cout << &str << endl;
return 0;
}
正确示例3:使用指针的引用
#include<iostream>
void GetMemory(char* &p, int num) {
p = (char*)malloc(sizeof(char)*num);
}
int main() {
char* str = NULL;
GetMemory(str, 100);
strcpy(str, "hello");
return 0;
}
2.下面函数有什么问题?
char* strA() {
char str[] = "hello world";
return str;
}
解析:
因为这个函数返回的是局部变量的地址,当调用这个函数后,这个局部变量str就释放了,所以返回的结果是不确定的且不安全,随时都有被收回的可能。“hello world”数组自然也不能访问了。
可以考虑改成以下:
char* strA() {
char* str = "hello world";
return str;
}
char* str与char str[]的区别
局部数组是局部变量,对应的是内存的栈;指针变量是全局变量,对应的是内存中的全局区域。
故有:
char *c= "hello";
*c= "hello"; //错误
char c[]= "hello";
c[0]= 'e'; //正确
故也可以使用static开辟一段静态存储空间:
const char* strA() {
static char str[] = "hello world";
return str;
}
3.下面程序的输出结果?
#include<cstdio>
class A {
public:
A() {m_a = 1; m_b = 2;}
~A(){}
void fun() {printf("%d%d\n", m_a, m_b);}
private:
int m_a;
int m_b;
};
class B
{
public:
B(){m_c = 3;}
~B();
void fun() {printf("%d\n", m_c);}
private:
int m_c;
};
int main() {
A a;
B *pb = (B*)(&a);
pb->fun();
}
解释:
输出结果为1.
强行把A类的内存当做B来处理。pb->fun()调用的是B::fun()来打印m_c。而m_c是B类对象的唯一元素,对应的也就是A类内存中存放的第一个元素,也就是m_a。所以打印了1.
4.下面程序的输出?
#include<iostream>
#include<cstdio>
using namespace std;
class A
{
public:
int a;
A(){a=1;}
void print() {printf("%d", a);}
};
class B: public A
{
public:
int a;
B() {a=2;}
};
int main() {
B b;
b.print();
printf("%d", b.a);
}
解析:
B类中的a把A类中的a“隐藏”了。在构造B类时,先调用A类的构造函数。所以A类的a是1,B类的a是2.输出是12
4.函数指针与指针函数
指针函数 :指函数的返回值为指针的函数,一般是形如下的函数:
int* func(int x,int y);
函数指针:指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么指向这个函数的函数指针便是指向这个地址。
函数指针主要有两个作用:用作调用函数和做函数的参数。
int (*func)(int x);
1.示例程序:
#include<cstdio>
int max(int x, int y) {
return x>y?x:y;
}
int main() {
int (*p)(int, int) = &max; /* 函数指针 */
int a, b, c, d;
scanf("%d%d%d", &a, &b, &c);
d = (*p)((*p)(a, b), c); /* 调用方式 */
printf("%d\n", d);
}
2.示例程序:
5.数组指针和指针数组
判断方法:看谁优先级高,就是什么
例如:int (*ptr)[]
优先级高的是*ptr 所以是一个指针,一个指向int类型数组指针;
int *ptr[]
与int *(ptr[])
因为[]优先级高于*,所以是一个数组,一个放着指针的数组。
1.下面程序的输出?
#include<cstdio>
#include<iostream>
using namespace std;
int main() {
int v[2][10] = {{1,2,3,4,5,6,7,8,9,10}, {11,12,13,14,15,16,17,18,19,20}};
int (*a)[10] = v;
cout << **a << endl;
cout << **(a+1) << endl;
cout << *(*a+1) << endl;
cout << *(a[0]+1) << endl;
cout << *(a[1]) << endl;
}
解析:
1 11 2 2 11
a是一个指向10个int数组的指针,因此a+1表明指针向后移动1*sizeof(数组大小)
2.下面程序的输出?
#include<cstdio>
#include<iostream>
using namespace std;
int main() {
int a[]={1,2,3,4,5};
int *ptr = (int*)(&a+1);
cout << *(a+1) << " " << *(ptr-1) << endl;
}
解析:
2 5
a是一个5个元素的数组,(&a+1)是5元素数组的下一个数组的首地址,也就是’5’这个元素后面的一个位置,所以*(ptr-1)的值就是5了。
数组名本身就是指针,再加个&,就变成了双指针,这里的双指针就是二维数组,加1,就是数组整体加一行,ptr指向a的第6个元素
3.下面数据声明代表什么?
float(**def)[10]
def是一个二级指针,指向一个一级指针,这个一级指针指向一个float数组
double*(*gh)[10]
gh是一个一级指针,指向一个double*数组
double(*f[10])()
f是一个数组,数组的元素是函数指针,函数形参为空,返回值为double
int*((*b)[10])
b是一个指针,指向一个数组,数组元素是int*
long (*fun)(int)
fun是一个函数指针,函数返回值是long,参数是一个int
int (*(*F)(int, int))(int)
F是一个函数指针,该函数的参数是(int,int),返回值是一个函数指针,这个返回的函数指针参数是一个int,返回值是一个int
this指针
- this指针本质是一个函数参数,只能在成员函数中使用,全局函数、静态函数都不能使用this。
- this在成员函数的开始执行前构造,在成员的执行结束后清除。
- this指针会因编译器不同而有不同的放置位置。可能是堆、栈,也可能是寄存器。
- this是指向对象的“常指针”,因此无法改变。它是一个指向相应对象的指针。
- this指针只有在成员函数中才有定义。因此,你获得一个对象后,也不能通过对象使用this指针。
迷途指针,野指针
野指针: 声明一个指针(例如
int *p;
),编译器会随机分配一个内存地址给它,但是,此地址不可用(如cout<<p;
此句会出现运行时错误,见图1,但是编译会通过)。迷途指针:
int *p = new int; delete p;
此时的p指针就是一个迷途指针。为了避免出现迷途指针,记得再加上p= NULL;
malloc,free与new, delete
- malloc与free是C++/C 语言的标准库函数,new/delete 是C++的运算符。
对于非内部数据类的对象而言,光用maloc/free 无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加malloc/free。
外:
内部数据类型是编译器本来就认识的,不需要用户自己定义 。
非内部数据类型不是编译器本来就认识的,需要用户自己定义才能让编译器识别 。
例如:由enum,union,class、struct等关键字修饰的变量基本数据类型是非内部数据类型;由int,char,double等都是内部数据类型, 用它们定义的变量便是相应的对象.
— From 《程序员面试宝典》