C 和 C++ 基础

extern 关键字作用

extern 可用置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外 extern 也可以用来进行链接指定。

简单来说 extern 有两个作用:
1.链接指定,当 extern 与 “C” 一起连用时,是告诉编译器在编译函数时,按照C的规则去编译相应的函数名,而不是按照C++的规则。因为C++支持函数重载,会使得函数名“面目全非”,在某些场合下,这会影响到链接器的工作。

有以下两种方式来规定函数的编译规则:

extern "C" void func1();

extern "C" 
{
void func2();
}

有 “C” 相应的就有 “C++”。但一般没地方会用到这个,因为只有 C++ 的编译器会支持这种语法

extern "C++" void func1();

extern "C++" 
{
void func2();
}

当编程员自己也不确定使用的编译器是C的还是C++时,采用 __cplusplus 宏可以处理这种情况。

#ifdef __cplusplus
extern "C" 
{
#endif

// 代码...

#ifdef __cplusplus
}
#endif

2.修饰变量,告知编译器这个变量是在其他地方声明或定义过的全局变量,在链接时再去寻找该变量。

这里再简单讲述一下变量存储类型,以便于了解 extern。

关键字变量存储类型
auto自动变量:在函数中的所有非静态局部变量。变量存储在动态存储区(堆或栈)中。
static静态变量:包括全局静态变量和局部静态变量。变量存储在静态存储区。
extern外部变量: 全局变量。变量存储在静态存储区。
register寄存器变量:变量存储在寄存器中,读写快。

C++11已经弃用 register,使用多的变量会被编译器自动优化存放位置,而auto被赋予了自动类型推导的功能。


static 关键字作用

1.修饰局部变量,延长变量的生命周期。
2.修饰全局变量,隐藏该全局变量,使得该变量只能被本文件所访问。
3.修饰函数,隐藏该函数,使得该函数只能被本文件所调用。
4.修饰类成员数据,使得该成员数据被整个类共享,而不只属于某个单一对象。


volatile 关键字作用

volatile 有三大特性,如下:
1.易变形。在汇编层面上,如果有访问同一 volatile 变量的两条语句,第二条语句不会直接使用上一条语句对应的 volatile 变量的寄存器内容,而是直接从内存中读取。
2.不可优化性,字面意思,编译器不会对 volatile 变量进行过激的优化。
3.顺序性,C/C++ Volatile关键词前面提到的两个特性,让Volatile经常被解读为一个为多线程而生的关键词:一个全局变量,会被多线程同时访问/修改,那么线程内部,就不能假设此变量的不变性,并且基于此假设,来做一些程序设计。当然,这样的假设,本身并没有什么问题,多线程编程,并发访问/修改的全局变量,通常都会建议加上Volatile关键词修饰,来防止C/C++编译器进行不必要的优化。

volatile用在如下的几个地方:
1.中断服务程序中修改的供其它程序检测的变量需要加volatile。
2.多任务环境下各任务间共享的标志应该加volatile。
3.存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义。


const 关键字作用

1.修饰指针变量

const char* p;	// 指针指向的值不能改变
char* const p;	// 指针的指向不能改变(p 本身存放得地址不能改变)

2.修饰成员变量和成员函数

class A
{
private:
	const int a;	// 此成员变量不能被修改
public:
const int func1(int a, int b);	// 返回值不能改变(返回值不能做左值)
int func2(const int a, const int b);	// 函数参数不能被改变
int func3(int a, int b) const;	// 类成员不能被此函数修改
};

new 与 malloc 的区别

/*
malloc和free是库函数,new和delete是C++操作符
new自己计算需要的空间大小和返回的指针类型;malloc需要指定大小,返回 void*
new在动态分配内存的时候初始化对象,调用其构造函数,delete调用析构函数;malloc只是返回指向内存的指针
new和delete可以进行操作符重载
*/
class A
{
};

int main()
{
	A* a = new A;
	A* b = (A*)malloc(sizeof(A));	// 注意此时,sizeof(A) = 1
	
	A* c = new A[10];
	A* d = (A*)malloc(sizeof(A) * 10);
}

C++的多态性和虚函数表

多态性(polymorphism),简单地来说是“一个接口,多种方法”。
C++的多态性是通过重写(override)和重载(overload)实现的。
重写通过虚函数实现,虚函数允许子类重新定义成员函数。
重载主要通过编译器实现,编译会为同名函数(参数或返回值不同)重新命名以便在链接时能够区分出函数的不同。
多态的目的是为了接口重用,无论是什么对象,都能通过同一接口找到合适的实现方法。

// overload
int  func(int a)
{
	return a;
}

int  func(int a, int b)
{
	return a + b;
}

// override
class A
{
public:
	void func1()
	{
		cout << 1 << endl;
	}
	
	virtual void func2()
	{
		cout << 2 << endl;
	}
	virtual ~A(){}
};

class B : public A
{
public:
	void func1()
	{
		cout << 3 << endl;
	}
	void func2()
	{
		cout << 4 << endl;
	}
};

int main()
{
	A a;
	B b;
	A* p;
	p = &a;
	p->func1();
	p->func2();
	p = &b;
	p->func1();
	p->func2();
	cout << func(1) << endl;
	cout << func(1, 1) << endl;
	return 0;
}

结果应当为:

1
2
1
4
1
2

纯虚函数

定义:
纯虚函数是在其基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。

// 有纯虚函数的类称之为纯虚类(抽象类)
// 纯虚类不能实例化
class A
{
public:
	virtual void print() = 0;
	virtual ~A(){}
};

class B : public A
{
public:
	// 子类必须完成父类纯虚函数的实现方法
	void print()
	{
		cout << 1 << endl;
	}
	~B(){}
};

虚函数表

虚函数是通过虚函数表来实现的。
虚函数表其实是一张包含各种虚函数地址(虚函数指针)的表。C++编译器保证虚函数表的指针存在于对象实例中最前面的位置。
虚函数按照其声明顺序放于表中,父类的虚函数在子类的虚函数前面。如果子类有重写父类的虚函数,会将相应的虚函数覆盖(父类子类的各自维护自己的虚函数表)。
每个类会维护一张虚函数表,每个对象会有指向虚函数表的指针。

#include <iostream>
using namespace std;

class A
{
public:
    virtual void print()
    {
        cout << "A: print()" << endl;
    }

protected:
    virtual void test()
    {
        cout << "A: test()" << endl;
    }
};

class B : public A
{
public:
    void print()
    {
        cout << "B: print()" << endl;
    }
};

typedef void (* pFunc) ();

void AAA()
{
    cout << "AAA()" << endl;
}

int main()
{  
    cout << sizeof(A) << endl;
    cout << sizeof(B) << endl;
    A* a = new A;
    B* b = new B;
    pFunc pfunc1 = NULL, pfunc2 = NULL, pfunc3 = NULL;
    /*
	A a
	(int*)(&a) 虚函数表的指针
	*((int*)(&a)) 指向虚函数表的第一项(虚函数表的开始地址)
	(int*)*((int*)(&a)) 把虚函数表的第一项视作指针
	*((int*)*((int*)(&a))) 虚函数表的第一个指针指向的东西(虚函数)
	 */
    pfunc1 = (pFunc)*((int*)*((int*)(a)));
    pfunc2 = (pFunc)*((int*)*((int*)(b)));
    // 64位环境下,指针的长度为8字节,2个int类型的长度
    pfunc3 = (pFunc)*((int*)*((int*)(b)) + 2);
    pfunc1();
    pfunc2();
    pfunc3();
    delete a;
    delete b;
    a = NULL;
    b = NULL;
    return 0;
}

输出结果如下(64位环境):

8
8
A: print()
B: print()
A: test()


未完待续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值