C++学习(二)

    技术2022-07-10  174

    C++学习(一)C++学习(三)

    目录二

    30 strlen()与sizeof31 #ifndef/#define/#endif使用详解32 指针和数组33 友元34 this指针引言1、this指针的概念1.1、定义1.2、this只能在成员函数中使用1.3、this指针不能再静态函数中使用1.4、this指针的创建1.5、this指针只有在成员函数中才有定义。 2、this指针的操作 36 其他指针相关37 C语言获得系统微秒时间38 指针和数组的关系39 const一个函数名后面加const表示什么意思? 40 c++中为什么可以通过指针或引用实现多态,而不可以通过对象呢?41 指针函数 & 函数指针指针函数函数指针 42 C++各种可调用对象仿函数Lambda函数 43 using 关键字44 常量指针 & 指针常量const关键字的作用常量指针指针常量

    30 strlen()与sizeof

    strlen只能用char*做参数,且该char数组必须是以’’/0’'结尾的。

    数组做sizeof的参数不退化,传递给strlen就退化为指针了。 详细说明可参见: 百度知道: strlen和sizeof有什么区别?

    经典C语言面试题8:sizeof与strlen的区别

    1、sizeof是C/C++中的一个运算符,其作用是返回一个对象或者类型在内存中所占用的字节数。

    注意:sizeof后面如果是类型则必须加括号,如 sizeof(char);而如果是变量名则可以不加括号,如 sizeof a; 但是建议使用时 均加上括号。sizeof不能返回动态地被分配的数组的大小。

    2、strlen是C语言中的库函数,所在头文件为#include <string.h>其函数原型为unsigned int strlen(char *s); 其中s为指定的字符串。

    注意:strlen只能用char *作为参数,它求的是字符串的实际长度,方法是从开始到遇到第一个’\0’结束。

    3、 sizeof是编译期就计算完成的,strlen是运行期计算的。

    注意:大部分编译程序 在编译的时候就把sizeof计算过了 是类型或是变量的长度这就是sizeof(x)可以用来定义数组维数的原因 。 所以,sizeof 即使在字符数组没有终止符’/0’ 的时候,也能够计算出数组“长度”的原因,但这里的“长度”实际上是:编译器分配给该数组变量的内存大小! 例如:char chs[] = {‘a’, ‘c’, ‘/0’, ‘z’, ‘3’,‘d’}; // sizeof(chs) = 6; 而strlen(chs) = 2.

    几个例子

    (1)

    char str[20] = "0123456789"; int a = strlen(str); /*a = 10*/ int b = sizeof(str);/*b = 20*/

    上面结果为a = 10,这是因为strlen计算的是字符串的实际长度,以第一个’\0’为结束符;b = 20,这是因为sizeof计算的是分配的数组str[20]所占的空间大小,不受里面存储内容的影响。

    (2)

    char *ss = "0123456789"; int i = sizeof(ss); /*i = 4*/ int j = sizeof(*ss); /*j = 1*/ int k = strlen(ss); /*k = 10*/

    上面结果为i = 4,这是因为sizeof获得的是一个指针的值所占的地址空间,为long int型的,占4个字节;j = 1,这是因为 *ss 为第一个字符 ‘0’ 所以占一个字节;k = 10,这是因为strlen所获得的是此字符串的实际长度,所以要想获得此字符串的长度,则一定要用strlen。

    (3)

    char buffer[] = "Hello"; int m = strlen(buffer); /*m = 5*/ int n = sizeof(buffer); /*n = 6*/

    上面结果为m = 5,这是因为strlen测量的是字符串的实际长度,以遇到的第一个’\0’为结束标志;n = 6,这是因为字符串以’\0’为结束标志,’\0’也需要占用一个字节,所以sizeof测得的结果为6。

    sizeof()详细用法

    Reference C语言中,当计算字符数组长度时,用sizeof 和strlen 的原理及两者的区别

    31 #ifndef/#define/#endif使用详解

    #pragma once用法总结

    头文件被重复引用引起的后果:

    有些头文件重复引用只是增加了编译工作的工作量,不会引起太大的问题,仅仅是编译效率低一些,但是对于大工程而言编译效率低下那将是一件多么痛苦的事情。 有些头文件重复包含,会引起错误,比如在头文件中定义了全局变量(虽然这种方式不被推荐,但确实是C规范允许的)这种会引起重复定义。

    是不是所有的头文件中都要加入#ifndef/#define/#endif 这些代码?

    答案:不是一定要加,但是不管怎样,用#ifnde xxx #define xxx #endif或者其他方式避免头文件重复包含,只有好处没有坏处。个人觉得培养一个好的编程习惯是学习编程的一个重要分支。

    下面给一个#ifndef/#define/#endif的格式:

    #ifndef A_H意思是"if not define a.h" 如果不存在a.h

    接着的语句应该#define A_H 就引入a.h

    最后一句应该写#endif 否则不需要引入

    Reference #ifndef/#define/#endif使用详解

    32 指针和数组

    数组指针和指针数组的区别 https://blog.csdn.net/qq_31504597/article/details/79966023

    33 友元

    在C++中,我们使用类对数据进行了隐藏和封装,类的数据成员一般都定义为私有成员,成员函数一般都定义为公有的,以此提供类与外界的通讯接口。但是,有时需要定义一些函数,这些函数不是类的一部分,但又需要频繁地访问类的数据成员,这时可以将这些函数定义为该函数的友元函数。除了友元函数外,还有友元类,两者统称为友元。友元的作用是提高了程序的运行效率(即减少了类型检查和安全性检查等都需要时间开销),但它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。

    友元函数

    友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend,其格式如下: friend 类型 函数名(形式参数); 友元函数的声明可以放在类的私有部分,也可以放在公有部分,它们是没有区别的,都说明是该类的一个友元函数。 一个函数可以是多个类的友元函数,只需要在各个类中分别声明。 友元函数的调用与一般函数的调用方式和原理一致。

    友元类 将外界的某个类在本类别的定义中说明为友元,那么外界的类就成为本类的“朋友”,那个类就可以访问本类的私有数据了。友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。 当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。定义友元类的语句格式如下: friend class 类名; 其中:friend和class是关键字,类名必须是程序中的一个已定义过的类。 例如,以下语句说明类B是类A的友元类: class A { … public: friend class B; … }; 经过以上说明后,类B的所有成员函数都是类A的友元函数,能存取类A的私有成员和保护成员。

    使用友元类时注意: (1) 友元关系不能被继承。 (2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。 (3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明

    《windows环境多线程编程原理与应用》中解释:   如果将类的封装比喻成一堵墙的话,那么友元机制就像墙上了开了一个门,那些得   到允许的类或函数允许通过这个门访问一般的类或者函数无法访问的私有属性和方      法。友元机制使类的封装性得到消弱,所以使用时一定要慎重。

     class Merchant { private : int m_MyMoney; int m_MyRoom; … … Public: Friend class Lawyer; Int getmoney(); … … }; ******************************************** class Lawyer { Private: … … Public: … … };

    只有你赋予某个类为你的友元时,那个类才有访问你的私有数据的权利。说明一个函数为一个类的友元函数则该函数可以访问此类的私有数据和方法。定义方法是在类的定义中,在函数名前加上关键字friend.

    需要友元与友元的优缺点:

    通常对于普通函数来说,要访问类的保护成员是不可能的,如果想这么做那么必须把类的成员都生命成为public(共用的),然而这做带来的问题遍是任何外部函数都可以毫无约束的访问它操作它,c++利用friend修饰符,可以让一些你设定的函数能够对这些保护数据进行操作,避免把类成员全部设置成public,最大限度的保护数据成员的安全。

    友元能够使得普通函数直接访问类的保护数据,避免了类成员函数的频繁调用,可以节约处理器开销,提高程序的效率,但所矛盾的是,即使是最大限度大保护,同样也破坏了类的封装特性,这即是友元的缺点,在现在cpu速度越来越快的今天我们并不推荐使用它,但它作为c++一个必要的知识点,一个完整的组成部分,我们还是需要讨论一下的。 在类里声明一个普通数学,在前面加上friend修饰,那么这个函数就成了该类的友元,可以访问该类的一切成员。

    示例1 #include <iostream> #include <cstring> using namespace std; class Internet { public: Internet(char *name,char *address) // 改为:internet(const char *name , const char *address) { strcpy(Internet::name,name); strcpy(Internet::address,address); } friend void ShowN(Internet &obj); //友元函数的声明 public:// 改为:private char name[20]; char address[20]; }; void ShowN(Internet &obj) //类外普通函数定义,访问a对象的保护成员name,不能写成,void Internet::ShowN(Internet &obj) { cout<<obj.name<<endl; //可访问internet类中的成员 } int main() { Internet a("谷歌","http://www.google.cn/";); ShowN(a); cin.get(); } 示例2 分别定义一个类A和类B ,各有一个私有整数成员变量通过构造函数初始化;类A有一个成员函数Show(B &b)用来打印A和B的私有成员变量,请分别通过友元函数和友元类来实现此功能。使用友元类 和 友元函数实现: #include <iostream> using namespace std; class B; class A; void Show( A& , B& ); class B { private: int tt; friend class A; friend void Show( A& , B& ); public: B( int temp = 100):tt ( temp ){} }; class A { private: int value; friend void Show( A& , B& ); public: A(int temp = 200 ):value ( temp ){} void Show( B &b ) { cout << value << endl; cout << b.tt << endl; } }; void Show( A& a, B& b ) { cout << a.value << endl; cout << b .tt << endl; } int main() { A a; B b; a.Show( b ); Show( a, b ); return 0; }

    34 this指针

    C++ this指针

    引言

    首先,我们都知道类的成员函数可以访问类的数据(限定符只是限定于类外的一些操作,类内的一切对于成员函数来说都是透明的),那么成员函数如何知道哪个对象的数据成员要被操作呢,原因在于每个对象都拥有一个指针:this指针,通过this指针来访问自己的地址。 注意: this指针并不是对象的一部分,this指针所占的内存大小是不会反应在sizeof操作符上的。this指针的类型取决于使用this指针的成员函数类型以及对象类型,

    1、this指针的概念

    1.1、定义

    在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。

    1.2、this只能在成员函数中使用

    成员函数默认第一个参数为T* const register this。 (友元函数,全局函数不是成员函数)

    1.3、this指针不能再静态函数中使用

    静态函数如同静态变量一样,他不属于具体的哪一个对象,静态函数表示了整个类范围意义上的信息,而this指针却实实在在的对应一个对象,所以this指针不能被静态函数使用。

    1.4、this指针的创建

    this指针在成员函数的开始执行前构造的,在成员的执行结束后清除。

    1.5、this指针只有在成员函数中才有定义。

    创建一个对象后,不能通过对象使用this指针。也无法知道一个对象的this指针的位置(只有在成员函数里才有this指针的位置)。当然,在成员函数里,你是可以知道this指针的位置的(可以&this获得),也可以直接使用的。

    2、this指针的操作

    在类的非静态成员函数中返回类对象本身的时候,我们可以使用圆点运算符*,箭头运算符->。 例一:

    #include<iostream> using namespace std; class A{ private: int x; public: A(){ x=0; } void display(){ cout<<this<<endl; cout<<this->x<<endl; cout<<x<<endl; cout<<(*this).x<<endl; } }; int main(void){ A a; a.display(); }

    输出:

    0x70fe40 0 0 0

    关于this指针的一个精典回答: 当你进入一个房子后, 你可以看见桌子、椅子、地板等, 但是房子你是看不到全貌了。 对于一个类的实例来说, 你可以看到它的成员函数、成员变量, 但是实例本身呢? this是一个指针,它时时刻刻指向你这个实例本身。

    36 其他指针相关

    Reference c语言指针作为函数的参数

    37 C语言获得系统微秒时间

    #include <stdio.h> #include <sys/time.h> #include <unistd.h> int main() { struct timeval start, end; gettimeofday( &start, NULL ); sleep(3); gettimeofday( &end, NULL ); int timeuse = (1000000 * ( end.tv_sec - start.tv_sec ) + end.tv_usec - start.tv_usec)/1000; printf("time: %d ms\n", timeuse); return 0; } #include<iostream> #include <stdlib.h> #include <stdio.h> #include <sys/time.h> #include <unistd.h> int main(){ struct timeval tv; gettimeofday(&tv,NULL); printf("second:%ld\n",tv.tv_sec); //秒 printf("millisecond:%ld\n",tv.tv_sec*1000 + tv.tv_usec/1000); //毫秒 printf("microsecond:%ld\n",tv.tv_sec*1000000 + tv.tv_usec); //微秒 sleep(3); // 为方便观看,让程序睡三秒后对比 std::cout << "3s later:" << std::endl; gettimeofday(&tv,NULL); printf("second:%ld\n",tv.tv_sec); //秒 printf("millisecond:%ld\n",tv.tv_sec*1000 + tv.tv_usec/1000); //毫秒 printf("microsecond:%ld\n",tv.tv_sec*1000000 + tv.tv_usec); //微秒 return 0; }

    38 指针和数组的关系

    导入(C primer plus P911)

    我们试着用 malloc()创建一个数组。除了用 malloc()在程序运行时请求一块内存,还需要一个指针记录这块内存的位置。例如,考虑下面的代码:

    double * ptd; ptd = (double *) malloc(30 * sizeof(double));

    以上代码为30个double类型的值请求内存空间,并设置ptd指向该位置。 注意,指针ptd被声明为指向一个double类型,而不是指向内含30个double类型值的块。回忆一下,数组名是该数组首元素的地址。因此,如果让ptd指向这个块的首元素,便可像使用数组名一样使用它。也就是说,可以使用表达式ptd[0]访问该块的首元素,ptd[1]访问第2个元素,以此类推。根据前面所学的知识,可以使用数组名来表示指针,也可以用指针来表示数组。

    #include<stdio.h> int* res; void test(int* b); int main() { int a[]={1,2,3,4,-1,-2,-3,-4,2,3}; test(a); for(int i = 0; i < 10; ++i){ printf("res[%d] = %d\n",i, res[i]); } } void test(int* b) { res = b;//指针b和res为指向数组a的首元素地址,均可以当数组来使用,但注意其sizeof还是有区别 printf("传进来的数组总字节数= %d\n", sizeof(res));//传进来的数组总字节数= 8,指针在64位系统中为8字节 } #include<stdio.h> int res[20]; void test(int* b); int main() { int a[]={1,2,3,4,-1,-2,-3,-4,2,3}; test(a); for(int i = 0; i < 10; ++i){ printf("res[%d] = %d\n",i, res[i]); } } void test(int* b) { for(int i = 0; i < 10; ++i){ res[i] = b[i];//指针b指向数组a的首地址,可以当数组用 } }

    39 const

    一个函数名后面加const表示什么意思?

    这是把整个函数修饰为const,意思是“函数体内不能对成员数据做任何改动”。如果你声明这个类的一个const实例,那么它就只能调用有const修饰的函数。

    class Text{ public: void printconst(void)const{cout<<"hello"<<endl;} void print(void){cout<<"hello"<<endl;} private: int k; }; const Text a; //上面定义了类Text的一常量对象 int main(void) { a.printconst(); //ok a.print(); //error //上面a.print()调用是非法的 return 0; } const对象只能调用const成员函数。const对象的值不能被修改,在const成员函数中修改const对象数据成员的值是语法错误 。在const函数中调用非const成员函数是语法错误

    40 c++中为什么可以通过指针或引用实现多态,而不可以通过对象呢?

    c++中为什么可以通过指针或引用实现多态,而不可以通过对象呢?

    41 指针函数 & 函数指针

    参考

    函数指针和指针函数用法和区别面试题21:调整数组顺序使奇数位于偶数前面

    指针函数

    定义

    指针函数,简单的来说,就是一个返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。

    声明格式为:*类型标识符 函数名(参数表)

    指针函数的写法

    int *fun(int x,int y); int * fun(int x,int y)int* fun(int x,int y);

    这个写法看个人习惯,其实如果*靠近返回值类型的话可能更容易理解其定义。

    函数指针

    定义

    函数指针,其本质是一个指针变量,该指针指向这个函数。总结来说,函数指针 就是指向函数的指针。

    声明格式:类型说明符 (*函数名) (参数)

    int (*fun)(int x,int y);

    函数指针是需要把一个函数的地址赋值给它,有两种写法:

    fun = &Function; fun = Function;

    取地址运算符&不是必需的,因为一个函数标识符 就表示了它的地址,如果是函数调用,还必须包含一个圆括号括起来的参数表。

    调用函数指针的方式也有两种:

    x = (*fun)(); x = fun();

    两种方式均可,其中第二种看上去和普通的函数调用没啥区别。如果可以的话,建议使用第一种,因为可以清楚的指明这是通过指针的方式来调用函数。当然,也要看个人习惯,如果理解其定义,随便怎么用都行啦。

    42 C++各种可调用对象

    C++中的各种可调用对象

    普通函数类成员函数类静态函数仿函数函数指针lambda表达式 C++11加入标准std::function C++11加入标准

    仿函数

    什么是仿函数?

    仿函数(Functor)又称为函数对象(Function Object)是一个能行使函数功能的类。仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载 operator() 运算符。因为调用仿函数,实际上就是通过类对象调用重载后的 operator() 运算符。

    如果编程者要将某种“操作”当做算法的参数,一般有两种方法: (1)一个办法就是先将该“操作”设计为一个函数,再将函数指针当做算法的一个参数。上面的实例就是该做法; (2)将该“操作”设计为一个仿函数(就语言层面而言是个 class),再以该仿函数产生一个对象,并以此对象作为算法的一个参数。

    很明显第二种方法会更优秀,因为第一种方法扩展性较差,当函数参数有所变化,则无法兼容旧的代码,具体在第一小节已经阐述。正如上面的例子,在我们写代码时有时会发现有些功能代码,会不断地被使用。为了复用这些代码,实现为一个公共的函数是一个解决方法。不过函数用到的一些变量,可能是公共的全局变量。引入全局变量,容易出现同名冲突,不方便维护。

    这时就可以使用仿函数了,写一个简单类,除了维护类的基本成员函数外,只需要重载 operator() 运算符 。这样既可以免去对一些公共变量的维护,也可以使重复使用的代码独立出来,以便下次复用。而且相对于函数更优秀的性质,仿函数还可以进行依赖、组合与继承等,这样有利于资源的管理。如果再配合模板技术和 Policy 编程思想,则更加威力无穷,大家可以慢慢体会。Policy 表述了泛型函数和泛型类的一些可配置行为(通常都具有被经常使用的缺省值)。

    class ImitateAdd { public: int operator()(const int a, const int b) const { return a + b; }; }; int main() { // 首先创建一个仿函数对象,然后调用()运算符模拟函数调用 ImitateAdd imitate; imitate(5, 10); getchar(); return 0; }

    Lambda函数

    Lambda函数,又可以称为Lambda表达式或者匿名函数,在C++11中加入标准。定义形式如下:[captures] (params) -> return_type { statments;}

    其中:

    [captures]为捕获列表,用于捕获外层变量(params)为匿名函数参数列表->return_type指定匿名函数返回值类型{ statments; }部分为函数体,包括一系列语句

    需要注意:

    当匿名函数没有参数时,可以省略(params)部分当匿名函数体的返回值只有一个类型或者返回值为void时,可以省略->return_type部分定义匿名函数时,一般使用auto作为匿名函数类型

    下面都是有效的匿名函数定义

    auto func1 = [](int x, int y) -> int { return x + y; }; auto func2 = [](int x, int y) { return x > y; }; // 省略返回值类型 auto func3 = [] { global_ip = 0; }; // 省略参数部分

    Lambda函数捕获列表

    为了能够在Lambda函数中使用外部作用域中的变量,需要在[]中指定使用哪些变量。

    下面是各种捕获选项:

    [] 不捕获任何变量[&] 捕获外部作用域中所有变量,并作为引用在匿名函数体中使用[=] 捕获外部作用域中所有变量,并拷贝一份在匿名函数体中使用[x, &y] x按值捕获, y按引用捕获[&, x] x按值捕获. 其它变量按引用捕获[=, &y] y按引用捕获. 其它变量按值捕获[this] 捕获当前类中的this指针,如果已经使用了&或者=就默认添加此选项

    只有lambda函数没有指定任何捕获时,才可以显式转换成一个具有相同声明形式函数指针

    auto lambda_func_sum = [](int x, int y) { return x + y; }; // 定义lambda函数 void (*func_ptr)(int, int) = lambda_func_sum; // 将lambda函数赋值给函数指针 func_ptr(10, 20); // 调用函数指针

    43 using 关键字

    (1) using 声明 using 声明 (using declaration) 是将命名空间中单个名字注入到当前作用域(using语句所在的作用域)的机制,使得在当前作用域下访问另一个作用域下的成员时无需使用限定符 :: // ... { using std::map map<int, std::string> the_map; //ok } map<int, std::string> the_map2; //error

    using 声明将其它 namespace 的成员引入本命名空间的 当前作用域 (包括其嵌套作用域) 。一个 using 声明一次只引入一个命名空间成员,它使得无论程序中使用哪些名字,都非常准确。

    利用 using 声明,可以改变派生类对父类成员的访问控制:

    class Base{ protected: int bn1; int bn2; }; class Derived: private Base{ public: using Base::bn1; }; class DerivedAgain: public Derived{ }; int main(){ Derived d; DerivedAgain da; d.bn1 = 1; d.bn2 = 2; //error, 'bn2' is a private member of 'Base' da.bn1 = 3; //ok std::cout<<d.bn1<<std::endl; return 0; }

    尽管 Derived 对 base 是私有继承,但通过 using 声明,我们还是可以在 Derived 中访问其成员,且后续的继承同样不受 private 限定的影响。

    ** (2) using 指示 (引入命名空间)**

    using 指示 (using directive) 是使一个命名空间中的 所有 名字都在该作用域中可见的机制。这是最常用的方式了。需要注意的是命名冲突问题。

    #include <iostream> namespace n1{ int n1_member = 10; int m = 11; } int m = 12; int main(){ using namespace n1; std::cout<<n1_member<<std::endl; //std::cout<<m<<std::endl; //error 命名冲突 std::cout<<::m<<std::endl; int m = 13; //ok, 局部变量屏蔽命名空间变量 std::cout<<m<<std::endl; return 0; }

    Notice: 尽管 using指示很方便,但在实际工作中应该尽量避免:它一下子将另一个 namespace 中的成员全部引入了,一不小心就会出现命名空间污染问题。

    (3) 类型重定义,取代 typedef using alias = typename

    这是 C++11 中的新用法,比 typedef 更能表达别名的定义。

    using fun = void (*)(int, int); //typedef void (*fun)(int, int); //与上一句等价 using int16 = short; //typedef short int16; //与上一句等价 int main(){ std::cout<<sizeof(int16)<<std::endl; }

    在 C++98/03 中 ,typedef 重定义有一些限制,比如,模板。 我们想实现这样一个模板:将一个 int 映射到任意类型,类似于我们想表达这种效果:

    typedef std::map<int, int> map_int_t; typedef std::map<int, std::string> map_str_t; typedef std::map<int, bool> map_b_t; //... Others

    我们在 C++98/03 中必须这样写:

    template<typename Val> struct int_map{ typedef std::map<int, Val> type; }; int main(){ int_map<int>::type imap; return 0; }

    在C++11 中,我们可以使用 using 重定义模板

    template<typename Val> using int_map_t = std::map<int, Val>; int main(){ int_map_t<int> imap; return 0; }

    44 常量指针 & 指针常量

    C语言——常量指针、指针常量以及指向常量的指针常量三者区别详解

    const关键字的作用

    const的作用是告诉编译器某个值是不变的,可以理解成只读,对变量起到保护作用。

    const可以用于以下方面:

    (1)修饰普通变量 需要在一开始就进行初始化;

    (2) 修饰指针 根据在 * 前后可以分为常量指针和指针常量,当然也可以前后都修饰,如const int* a , int* const a,const int * const a。

    (3)修饰函数中的参数与返回值

    修饰函数中的参数:const在函数的参数中可以保护指针不被修改,如strcpy(char* des, const char* src);修饰函数的返回值:也可以保证返回值指针的内容不被修改,如 const char* getStr() 中,接收类型就必须是 const char *。

    (4)修饰类中的成员变量与成员函数

    修饰成员函数时,不能对其成员变量进行修改(本质是修饰this指针);且const修饰的成员函数可以被const,非const对象调用,但是普通成员函数只能被普通对象调用。修饰成员变量时,必须在构造函数列表里进行初始化。

    延伸用法:const + &

    如const string& s,在满足的引用传递的优点下,既可以保护别名不被修改,也可以 接收右值(接收右值的原因在于右值都是const属性且不可见,只有const传递能捕捉到)。

    常量指针

    指向常量的指针,顾名思义,就是指针指向的是常量,即,它不能指向变量,它指向的内容不能被改变,不能通过指针来修改它指向的内容,但是指针自身不是常量,它自身的值可以改变,从而指向另一个常量。

    //注意char const *p与const char *p效果相同。 void consttest(const char *p) { printf("p[1]=%c\n",p[1]); p=1;//正确 *(p+1)='a';//错误 }

    因为const修饰的是char,所以就是说:p所指向的内存地址所对应的值,是const,因此不可修改。但指针所指向的内存地址是可以修改的,因为其并不是const类型。

    指针常量

    指针本身是常量。它指向的地址是不可改变的,但地址里的内容可以通过指针改变。它指向的地址将伴其一生,直到生命周期结束。有一点需要注意的是,指针常量在定义时必须同时赋初值。

    void testconst(char *const p) { char *tmp="13213"; p=1;;//错误 p=tmp;;//错误 p[1]='a';//正确 *(p+1)='a';//正确 }

    *(指针)和 const(常量) 谁在前先读谁 ;象征着地址,const象征着内容;谁在前面谁就不允许改变。

    Processed: 0.011, SQL: 12