C++基本知识点(三)

1.extern和extern “C”

(1)extern

函数的声明extern关键字是可有可无的,因为函数本身不加修饰的话就是extern的。

下面分变量和函数来说明:
(1) 变量
 extern int a; //声明一个全局变量
 int a; //定义一个全局变量

extern int a = 0;//定义全局变量并给初值
 int a = 0; //定义全局变量并给初值
上面的四个只有第一个extern int a才是声明,其他的全是定义。
当你要引用一个全局变量时,你就要声明extern int a;这个时候extern不能省,否则就成定义了。

(2) 函数
函数也有声明和定义,但由于函数的声明和定义是有区别的,函数的定义是有函数体的,所以函数的声明和定义都可以将extern省略掉,反正其他文件也是知道这个函数是在其他地方定义的。

(2)extern “C”:

extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言(而不是C++)的方式进行编译。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。
  作为C语言的扩展,C++保留了一部分过程式语言的特点,因而它可以定义不属于任何类的全局变量和函数。但是,C++毕竟是一种面向对象的设计语言,为了支持函数的重载,C++对全局函数的处理方式有着明显的不同。
  首先看一下C++对类似C的函数是怎样编译的:
  作为面向对象的语言,C++为了支持函数重载,函数在被C++编译后在符号库中的名字与C语言的不同。假如某个函数的原型为void foo(int x, int y);该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生_foo_int_int之类的名字。_foo_int_int这样的名字是包含了函数名以及形参,C++就是靠这种机制来实现函数重载的。
  被extern “C”修饰的函数或者变量是按照C语言方式编译和链接的,所以可以用一句话来概括extern “C”的真实目的:实现C++与C的混合编程。

2. 顶层const和底层const

首先,const是一个限定符,被它修饰的变量的值不能改变。对于一般的变量来说,其实没有顶层const和底层const的区别,而只有向指针这类复合类型的基本变量,才有这样的区别。

一 如何区分顶层const和底层const
指针如果添加const修饰符时有两种情况:

1 指向常量的指针:

代表不能改变其指向内容的指针。声明时const可以放在类型名前后都可,拿int类型来说,声明时:const int和int const 是等价的。声明指向常量的指针也就是底层const,下面举一个例子:
int num_a = 1;
int const *p_a = &num_a; //底层const
//*p_a = 2; //错误,指向“常量”的指针不能改变所指的对象

注意:指向“常量”的指针不代表它所指向的内容一定是常量,只是代表不能通过解引用符(操作符*)来改变它所指向的内容。上例中指针p_a指向的内容就不是常量,可以通过赋值语句:num_a=2; 来改变它所指向的内容。

2 指针常量:

代表指针本身是常量,声明时必须初始化,之后它存储的地址值就不能再改变。声明时const必须放在指针符号*后面,即:*const 。声明常量指针就是顶层const,下面举一个例子:

int num_b = 2;
int *const p_b = &num_b; //顶层const
//p_b = &num_a; //错误,常量指针不能改变存储的地址值
其实顶层const和底层const很简单,一个指针本身添加const限定符就是顶层const,而指针所指的对象添加const限定符就是底层const。

引用和指针

指针指向一块内存,它的内容是内存中的地址,而引用则是某个内存的别名,它的指向不改变。
①从现象上看,指针在运行时可以改变其所指向的值,而引用一旦和某个对象绑定后就不再改变。这句话可以理解为:指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定的对象其内容可以改变。
②从内存分配上看,程序为指针变量分配内存区域,而不为引用分配内存区域,因为引用声明时必须初始化,从而指向一个已经存在的对象。引用不能指向空值。
引用的效率高。

const修饰成员函数

(1)const修饰的成员函数不能修改任何的成员变量(mutable修饰的变量除外)

(2)const成员函数不能调用非const成员函数,因为非const成员函数可以会修改成员变量

#include <iostream>
using namespace std;
class Point{
    public :
    Point(int _x):x(_x){}

    void testConstFunction(int _x) const{

        ///错误,在const成员函数中,不能修改任何类成员变量
        x=_x;

        ///错误,const成员函数不能调用非onst成员函数,因为非const成员函数可以会修改成员变量
        modify_x(_x);
    }

    void modify_x(int _x){
        x=_x;
    }

    int x;
};
#include<iostream>
using namespace std;
  	int i=3;
	void func(int *p){
	i=5;
	p=&i;
	}

int main(int argc,char * argv[]){
	int j=10;
	int *ptr=&j;
	func(ptr);
	cout<<*ptr<<endl;
}

解析:在func中,拷贝了ptr的值,因此func中的指针p是指针ptr的一份拷贝,改变p的指向不影响ptr的指向。因此返回后ptr仍然指向j。

3.c++ 11新特性:

C++11相比C++98增加了许多关键字及新的语法特性。
auto关键字

nullptr关键字

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
class Test
{
public:
    void TestWork(int index)
    {
        std::cout << "TestWork 1" << std::endl;
    }
    void TestWork(int * index)
    {
        std::cout << "TestWork 2" << std::endl;
    }
};

int main()
{
    Test test;
    test.TestWork(NULL);
    test.TestWork(nullptr);
}

在这里插入图片描述
for循环语法

//示例代码1.0 http://www.cnblogs.com/feng-sc/p/5710724.html
int main()
{
    int numbers[] = { 1,2,3,4,5 };
    std::cout << "numbers:" << std::endl;
    for (auto number : numbers)
    {
        std::cout << number << std::endl;
    }
}

STL容器:
std::array
std::forward_list:std::forward_list为从++新增的线性表,与list区别在于它是单向链表
std::unordered_map:std::unordered_map与std::map用法基本差不多,但STL在内部实现上有很大不同,std::map使用的数据结构为二叉树,而std::unordered_map内部是哈希表的实现方式,哈希map理论上查找效率为O(1)。但在存储效率上,哈希map需要增加哈希表的内存开销。

多线程:
在C++11以前,C++的多线程编程均需依赖系统或第三方接口实现,一定程度上影响了代码的移植性。C++11中,引入了boost库中的多线程部分内容,形成C++标准,形成标准后的boost多线程编程部分接口基本没有变化,这样方便了以前使用boost接口开发的使用者切换使用C++标准接口,把容易把boost接口升级为C++接口。

std::thread
std::atomic:从功能上看,简单地说,原子数据类型不会发生数据竞争,能直接用在多线程中而不必我们用户对其进行添加互斥资源锁的类型。从实现上,大家可以理解为这些原子类型内部自己加了锁。
std::condition_variable:C++11中的std::condition_variable就像Linux下使用pthread_cond_wait和pthread_cond_signal一样,可以让线程休眠,直到别唤醒,现在在从新执行。线程等待在多线程编程中使用非常频繁,经常需要等待一些异步执行的条件的返回结果。

4.getMemory()

4.1

char *getMemory(void)
{ 
    char p[]="hello world";
    return p; 
}
 
void Test(void)
{ 
    char *str=NULL; 
    str=getMemory(); 
    printf(str); 
}

运行结果:运行无误,但打印乱码
解释:getMemory(void)中的p[]为函数内的局部自动变量,在函数返回后,内存已经被释放。如果一步步调试,会发现执行str=getMenory();后str不再是NULL了,但是str的内容并不是hello world,而是垃圾数据。

4.2

void getMemory(char **p,int num)
{
    *p=(char *)malloc(num);
}
 
void Test(void)
{
    char *str=NULL;
    getMemory(&str,100);
    strcpy(str,"hello world"); 
    printf(str); 
}

又会内存泄漏,同时还要检查内存是否有分配成功。

5 函数参数读取顺序:


int main()  
 
{  
 
int arr[] = {6,7,8,9,10};  
 
int *ptr = arr;  
 
*(ptr++) += 123;  
 
printf("%d ,%d/n",*ptr,*(++ptr));  
 
return 0;  
 
}

int arr[]={6,7,8,9,10};

int *ptr=arr;//现在ptr指向6

*(ptr++)+=123;//现在ptr指向7,第一个元素变为129

printf("%d,%d",ptr,(++ptr)); //考虑从右往左计算,先是*(++ptr),现在ptr指向8,然后*ptr也是8,输出8,8
printf的参数,函数printf从左往右读取,然后将先读取放到栈底,最后读取的放在栈顶,处理时候是从栈顶开始的,所有从右边开始处理的。

6 C C++ 结构体 以及C++中的类的区别

6.1 C C++ 结构体区别

C语言中的结构体不能为空。
C语言中的结构体只是一个复杂的数据类型,只能定义成员变量,不能定义成员函数,但是可以定义函数指针;C++可以定义成员函数和成员变量。C语言的结构体数据成员不能是结构体或者类,只能是简单的数据成员,而C++的数据成员可以使结构体或类。
C++的结构体和类体现了数据结构和算法的结合。
C语言中定义结构体变量时,struct不可以省略:C++中定义结构体变量时,struct可以省略。

struct Test
{
    int num;
    char ch;
};
 
int main()
{
    //C语言中
    struct Test t1;
    //C++中
    Test t2;
 
    return 0;
}

6.2 C++ 结构体类的区别

对于成员访问权限以及继承方式,class中默认private,struct中默认是public。
class可以用于表示模板类型,struct不行

//用模板的时候可以写
template <class Type>
template <typemname Type>
//不能写
template<struct Type>

猜你喜欢

转载自blog.csdn.net/huanghaihui_123/article/details/90230200