一. C&C++ 基础一

1.指针相关

Sample:
a. int *a[10] //指针数组
b. int (*a)[10] //数组指针(数组里面存放的是指针)
c. int (*a)(int) //指针函数
d. int (*a[10])(int) //函数指针数组(数组里存放的是函数指针)
e. int *a,int **a //指向a 的指针, 指向指针的指针

注意从右往左,由远到近,根据 * () [] 优先级来判断 。 ()>[]>* ,如 int a[10] []的优先级大于 ,所以首先是一个数组 , 由 * 修饰,所以是指针数组 。
运算符优先级

2. 指针和引用的区别

相同点:都在描述地址
指针: 指向一块内存, 存储的是内存的地址 。
引用:是一段内存的别名, 如张三的别名可以是 三儿 。

不同点
a .指针是一个实体,而引用是个别名 。
b. 指针可以指向任何的地址块, 引用在初始化后不可变 。
c 指针可以为空, 引用不可以为空 ,引用必须初始化。
d. 指针可以用const 修饰, 引用不可以 。
e. 指针和引用的自增++操作符意义不一样 。
指针++: 指向的内存的地址++
引用++: 引用的内存中的变量++
f. sizeof(引用) 得到的是引用的对象的实际大小 。 sizeof(指针)得到的是指向的内存的地址大小, 固定的 4 字节 。

作为形参传递指针与引用的区别:
void sample(int *a, int &b)
{
//1. 传入指针后, 在函数内部会产生一个临时的局部对象,int * temp = a ; 在函数内部操作的是临时变量 。 传入引用后,直接操作的就是原始对象。
//2. *a: 传入指针a , 是a 指向的内存的地址, 如 a 指向内存 "hello" , 函数内部无法修改hello 的内容 。 传入引用, 可以修改 “hello”的内容。
//3. 如果参数为 int *a , 调用时 &a,传入地址,也可达到传递引用的效果, 函数内部需使用(*a), 如 (*a) = 1;
}

作为函数返回值指针与引用的区别:
返回指针

//1. 函数不能返回局部变量的指针, **在返回指针时,在函数内部会产生一个返回对象的副本,而且返回的是对象副本(增大开销),在函数执行完毕后,原始对象的内存已经被释放,返回值所指向的是一个无效内存。**
char *getAddress()
{
    char *p = "hello" ;
    return p;
} 
//正确的返回指针方法,传入的是形参(在函数外部已经有内存), 返回的指针依旧指向实参 。
char *myString::strcat(char *des, char *src)
{
    assert(des != NULL);
    assert(src != NULL);
    char *temp = des;
    while (*des != '\0')
        des++;          //往前移动    是否应该考虑 des 大小?
    while ((*des++ = *src++) != '\0');
    return des;
}

返回引用

//在重载函数里操作的str 是一个类的成员变量,传入时已经分配内存 。返回*this 引用时, 函数内部不用创建一个临时对象,直接返回引用的对象。
myString & myString::operator=(const myString &st)
{
    if (strcmp(str,st.str)==0)  //this == &st
    {
        return *this;
    }
    delete[] str;
    len = st.len + 1;
    str = new char[len + 1];
    strcpy(str, st.str);
    return *this;
}

总结: 指针作为函数参数传递 或者返回时,在函数局部会创建一个临时对象,操作的实际时临时对象,这样无形中会增加内存开销;返回引用则不需要 。因此建议返回和传递引用。

3. const 相关

这里写图片描述

Const 是否占用内存?
常量有没有存储空间,或者只是编译时的符号而已?
不一定。
在C中,define常量是预处理阶段的工作,其不占据内存。但是const常量总是占据内存
在C++中,const常量是否占据存储空间取决于是否有引用该常量地址的代码。C++对于const默认为内部链接,因此const常量定义通常都放在头文件中,即使分配内存也不会出现链接错误。
若不引用常量对应的地址,则不会为其分配空间。

Const是用来替换define的,因此其必须能够放在头文件中,在C++中const变量是默认为内部链接的,即在其他文件中看不到本文件定义的const变量,因此链接不会出现问题。Const变量在定义的时候必须初始化,除非显式的指定其为extern的。通常C++中会尽量避免为const变量分配内存storage的,而是在编译阶段将其保存在符号表symbol table中。当用extern修饰const变量或引用其地址时,将强制为其分配内存,因为extern表示采用外部链接,因此其必须有某个地址保存其值。

#include <iostream.h>

const int i=100;   //无法找到i的符号,因为没有为其分配存储空间。
const int j=i+100;   //强迫编译器为常量分配存储空间
long address=(long)&j;
char buf[j+10];
int main(int argc, char* argv[])
{
  const char c=cin.get();
  const char d=3;  // 局部变量栈区
  char test1[d+10];
  //char test2[c+10];  error const char c必须到运行时刻动态获 取其初值。char test2[c+10]编译无法通过,因为无法确定c就无法确定数组长度。
  const char c2=c-'a'+'A';
  cout<<c<<" "<<c2<<endl;
  return 0;
}

const 使用:
/就近修饰原则,修饰离他最近的对象/
a. const char *p 常量字符
b. char const p char离他最近( 是指针) 常量字符
c. char * const p p 离他最近 常量指针
d. const int fun1() 这个其实无意义,因为参数返回本身就是赋值。
const int * fun2() 调用时 const int *pValue = fun2();
我们可以把fun2()看作成一个变量,即指针内容不可变。
int* const fun3() 调用时 int * const pValue = fun2();
我们可以把fun2()看作成一个变量,即指针本身不可变。
e. int Max() const 调用函数的对象不变
准确的说const是修饰this指向的对象的
譬如,我们定义了
classA{
public:
f(int);
};
这里f函数其实有两个参数,第一个是A*const this, 另一个才是int类型的参数
如果我们不想f函数改变参数的值,可以把函数原型改为f(constint),但如果我们不允许f改变this指向的对象呢?因为this是隐含参数,const没法直接修饰它,就加在函数的后面了,表示this的类型是constA *constthis。
const修饰*this是本质,至于说“表示该成员函数不会修改类的数据。否则会编译报错”之类的说法只是一个现象,根源就是因为*this是const类型的


一般情况下,函数的返回值为某个对象时,如果将其声明为const时,多用于操作符的重载。通常,不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况。原因如下:如果返回值为某个对象为const(const A test = A 实例)或某个对象的引用为const(const A& test = A实例) ,则返回值具有const属性,则返回实例只能访问类A中的公有(保护)数据成员和const成员函数,并且不允许对其进行赋值操作,这在一般情况下很少用到。

f. 类相关 const

(1)const修饰成员变量
const修饰类的成员函数,表示成员常量,不能被修改,同时它只能在初始化列表中赋值。
    class A
    {const int nValue;         //成员常量不能被修改
        …
        A(int x): nValue(x) { } ; //只能在初始化列表中赋值
     } 
(2)const修饰成员函数
const修饰类的成员函数,则该成员函数不能修改类中任何非const成员函数。一般写在函数的最后来修饰。
class A
    {void function()const; //常成员函数, 它不改变对象的成员变量.                        

//也不能调用类中任何非const成员函数。
} 

对于const类对象/指针/引用,只能调用类的const成员函数,因此,const修饰成员函数的最重要作用就是限制对于const对象的使用。

**a. const成员函数不被允许修改它所在对象的任何一个数据成员。
b. const成员函数能够访问对象的const成员,而其他成员函数不可以。**
(3)const修饰类对象/对象指针/对象引用

·             const修饰类对象表示该对象为常量对象,其中的任何成员都不能被修改。对于对象指针和对象引用也是一样。

·             const修饰的对象,该对象的任何非const成员函数都不能被调用,因为任何非const成员函数会有修改成员变量的企图。
例如:
class AAA
 { 
     void func1(); 
 void func2() const; 
 } 
 const AAA aObj; 
 aObj.func1(); ×
 aObj.func2(); 正确

const AAA* aObj = new AAA(); 
 aObj-> func1(); ×
 aObj-> func2(); 正确

g. 将Const类型转化为非Const类型的方法

采用const_cast 进行转换。
用法:const_cast (expression)
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。

· 常量指针被转化成非常量指针,并且仍然指向原来的对象;
· 常量引用被转换成非常量引用,并且仍然指向原来的对象;
· 常量对象被转换成非常量对象。

h. 使用const的一些建议
· 要大胆的使用const,这将给你带来无尽的益处,但前提是你必须搞清楚原委;

· 要避免最一般的赋值操作错误,如将const变量赋值,具体可见思考题;

· 在参数中使用const应该使用引用或指针,而不是一般的对象实例,原因同上;

· const在成员函数中的三种用法(参数、返回值、函数)要很好的使用;

· 不要轻易的将函数的返回值类型定为const;

· 除了重载操作符外一般不要将返回值类型定为对某个对象的const引用;

· 任何不会修改数据成员的函数都应该声明为const 类型。

i. 补充重要说明
· 类内部的常量限制:使用这种类内部的初始化语法的时候,常量必须是被一个常量表达式初始化的整型或枚举类型,而且必须是static和const形式。

· 如何初始化类内部的常量:一种方法就是static 和 const 并用,在外部初始化,例如:
class A { public: A() {} private: static const int i; file://注意必须是静态的! };
const int A::i=3;另一个很常见的方法就是初始化列表: class A { public: A(int i=0):test(i) {} private: const int i; }; 还有一种方式就是在外部初始化,

· 如果在非const成员函数中,this指针只是一个类类型的;如果在const成员函数中,
this指针是一个const类类型的;如果在volatile成员函数中,this指针就是一个
volatile类类型的。

· new返回的指针必须是const类型的。

4. static 相关

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值