C/C++知识点汇总

1.

C++中类对象的内存布局和占用空间:
a.非静态成员变量总合。

b.编译器为了CPU计算,作出的数据对齐处理(可用#pragma pack(n)来设定变量的对齐方式)。

c.为了支持虚函数,产生的额外负担。

//例子:
#pragma pack(2)
class BU
{
    int number;			//4
    union UBffer
    {
        char buffer[13];
        int number;
    }ubuf;			//13+1
    void foo(){}		//0
    typedef char*(*f)(void*);	//0
    enum{hdd,ssd,blueray}disk;	//4
}bu;
//sizeof(bu)的值为22。

重点:
空类在C语言中占0字节,在C++中占1字节。
sizeof(void):编译错误或者为1。sizeof(void*):指针大小。

2.
纯虚函数可以有函数体:

出处:《C++Primer》中文版第五版541页第22行。

我们可以为纯虚函数提供定义,不过函数体必须定义在类的外部。若定义在类的内部,会出现错误:pure-specifier on function-definition

class Dummy
{
	//error:pure_specifier on function-definition.
	virtual void process()=0{};
};


class Dummy
{
	virtual void process()=0;
}
void Dummy::process()
{}

3.
C++命名的强制类型转换(static_cast dynamic_cast const_cast reinterpret_cast):

cast_name<type>(expression)

A.static_cast:任何具有明确定义的类型转换,只要不包含底层const(比如常量指针,而不是指针常量),都可以使用。常用于窄化转换(告诉程序的读者和编译器:我们知道并且不在乎潜在的精度损失),编译器无法自动执行的类型转换(找回存在于void*指针中的值)。

a.用于类层次结构中基类和派生类之间指针或者引用的转换(up-casting把派生类的指针或引用转换成基类的指针或引用是安全的,down-casting把基类的指针或引用转换成派生类的指针或引用是不安全的)。

b.基本类型之间的转换。

c.把空指针转换成目标类型。

d.不能提供数字到指针的转换,不能提供不同类型指针之间的转换。


B.const_cast:只能改变运算对象的底层const(一旦我们去掉了某个对象的const性质,编译器就不再阻止我们对该对象进行写操作了。如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象是一个常量,再使用const_cast执行写操作就会产生未定义的后果)。只有const_cast能改变表达式的常量属性,使用其他形式的命名强制类型转换改变表达式的常量属性都将引发编译器错误。同样的,也不能用const_cast改变表达式的类型。常用于函数重载中

//比较两个string对象的长度,返回较短的那个引用.
const string &shortString(const string &s1,const string &s2)
{
	return s1.size()<=s2.size()?s1:s2;
}
/*当我们对两个非常量的string实参调用这个函数,但返回的结果仍然是const string的引用。因此我们需要一种新的shortString函数,当它的实参不是常量时,得到的结果是一个普通的引用,使用const_cast可以做到这一点:*/
string &shortString(string &s1,string &s2)
{
	//调用这个函数的目的:我们只要比较大小,这样调用是声明我们不能修改参数!!
	auto &r=shortString(const_cast<const string&>(s1),const_cast<const string&>(s2));
	return const_cast<string&>(r);
}


C.reinterpret_cast:通常为运算对象的位模式提供较低层次上的重新解释(数字到指针之间的转换,不同类型指针之间的转换)。操作结果只是简单的从一个指针到别的指针的值的二进制拷贝,在类型之间指向的内容不做任何类型的检查和转换。慎用!!


D.dynamic_cast:该转换符用于将一个指向派生类的基类指针或引用转换为派生类的指针或引用,注意dynamic_cast转换符只能用于含有虚函数的类。比如含有虚函数的基类B和从基类B派生出的派生类D,则B *pb; D *pd, d; pb=&d; pd=dynamic_cast<D*>(pb); 最后一条语句表示把指向派生类D的基类指针pb转换为派生类D的指针,然后将这个指针赋给派生类D的指针pd,有人可能会觉得这样做没有意义,既然指针pd要指向派生类为什么不pd=&d;这样做更直接呢?因为虚函数的基类版本和派生类版本必须具有相同的形参类型。就是当在派生类中覆盖虚函数时,若参数有基类指针或引用,需在函数里将参数强制转换为派生类指针或引用。也即有些时候我们需要强制转换,比如如果指向派生类的基类指针B想访问派生类D中的除虚函数之外的成员时就需要把该指针转换为指向派生类D的指针,以达到访问派生类D中特有的成员的目的,比如派生类D中含有特有的成员函数g(),这时可以这样来访问该成员dynamic_cast<D*>(pb)->g();因为dynamic_cast转换后的结果是一个指向派生类的指针,所以可以这样访问派生类中特有的成员。但是该语句不影响原来的指针的类型,即基类指针pb仍然是指向基类B的。如果单独使用该指针仍然不能访问派生类中特有的成员。


4.

a.text:已编译程序的机器代码。

b.data:已初始化的全局变量。

c.bss:未初始化的全局变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。目标文件格式区分初始化和未初始化变量是为了空间效率:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。

d.heap:

e.stack:


5.

A.通用多态

a.参数多态:模板。

b.包含多态:virtual。


B.特定多态

a.重载多态:

b.强制多态:强制类型转换。


6.

面向对象的三大基本特征:封装(数据抽象)、继承和多态(动态绑定)。

面向对象的五大基本原则:单一职责原则、开放封闭原则(扩展性开放,更改性封闭)、里氏替换原则、接口隔离原则和依赖倒置原则。


7.

对于free(p)这条语句,如果p是NULL指针,那么free对p无论操作多少次都不会出问题。如果p不是NULL指针,那么free对p连续操作两次就会导致程序运行错误。
fclose也类似。

8.

构造函数不能声明为虚函数,析构函数可以声明为虚函数,而且有时是必须声明为虚函数。不建议在构造函数和析构函数里面调用虚函数。


9.

val.operator++();		//前置++。
val.operator++(0);		//后置++。


10.

enum中:首元素不赋值的话,默认为0。后一个元素不赋值的话比前一个元素大1。


11.

所有的虚函数都必须有定义。


12.

数字前加0表示八进制数,加0x表示16进制。

-:左对齐。

:::作用域操作符。当作用域操作符的左侧为空时,向全局作用域发出请求。


13.

C语言:char a='a'; sizeof(char)=1;sizeof(a)=1;sizeof('a')=4;

C++语言:char a='a';sizeof(char)=1; sizeof(a)=1;sizeof('a')=1;
字符型变量是1字节这个没错,奇怪就奇怪在C语言认为'a'是4字节,而C++语言认为'a'是1字节。
原因如下:
C99标准的规定,'a'叫做整型字符常量(integer character constant),被看成是int型,所以在32位机器上占4字节。
ISO C++标准规定,'a'叫做字符字面量(character literal),被看成是char型,所以占1字节。

14.
在C++源文件中的语句前面加上extern "C",表明它按照类C的编译和连接规约来编译和连接,而不是C++的编译的连接规约。主要是解决在C++代码中调用C代码。

15.
关于浮点数相等问题:通过比较两数的差的绝对值是否小于FLT_EPSILON/DBL_EPSILON/LDBL_EPSILON。

16.
说使用引用有节省存储空间的作用是错误的。网上说并未规定要不要为引用分配空间,但自己通过结构体发现占用了空间!
int a=3;
class A
{
public:
    A():ra(a){}
private:
    int &ra;
};
//在64位系统中,sizeof(A)为8!

17.
volatile:易变的,不稳定的。volatile用来声明那些可能在你的程序本身不知道的情况下会发生改变的变量。

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。
对于一般变量:为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中。以后再取变量值时,就直接从寄存器中取值。

一个参数既可以是const也可以是volatile:一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。(简单点就是该程序代码不能试图去修改它,但不排除硬件方面修改了它,我们每次都得重新读取它的值。)

几个应用例子:
a. 并行设备的硬件寄存器(如:状态寄存器)。
b. 一个中断服务子程序中会访问到的非自动变量。(个人理解:中断服务子程序不能用缓存在寄存器中的值来判断事件,因为那个值可能被修改了,需要重新读取。所以一般需要把这种变量声明为volatile。)
c. 多线程应用中被几个任务共享的变量。

18.
构造函数的执行顺序:先执行基类的(如果基类当中有虚基类,要先执行虚基类的,都是按照继承时的顺序依次执行),再执行成员对象的(声明的顺序),最后执行自己的 。(切记都不是初始化列表的顺序)

19.
默认情况下,stack和queue是基于deque实现的,priority_queue是在vector之上实现的。

20.
声明多维数组只有最靠近数组名的那一维的大小可以省略。

21.
explicit???

22.
不能被重载的运算符:     .    ::    ?:    .*


补充(错题):

1.

对于一个运算符函数来说,它或者是类的成员(拥有this,即有一个类类型的参数),或者至少含有一个类类型的参数。

<pre class="cpp" name="code">int operator+(int,int);			错误,不能为int重定义内置的运算符。
MyClass operator+(int,int);		//不行。
MyClass operator+(int,MyClass);		//可以。
MyClass operator+(MyClass,int);		//可以。
MyClass operator+(MyClass,MyClass);	//可以。
 

2.

当我们把运算符定义成成员函数时,它的左侧运算对象必须是运算符所属类的一个对象。(出处:《C++Primer》中文版第五版493页第13行。)

如果我们想提供含有类对象的混合类型表达式,则运算符必须定义成非成员函数。唯一的要求是至少有一个运算对象是类类型。

//有如下类模板定义:
template<class T>
class BigNumber
{
	int n;
public:
	BigNumber(T i):n(i){}
	BigNumber operator+(BigNumber b)
	{
		return BigNumber(n+b.n);
	}
}; 
//已知b1,b2是BigNumber的两个对象,则下列表达式中错误的是?
//3+3
//b1+3
//b1+b2
//3+b2(错误!)


3.

//赋值运算符优先级比逗号运算符优先级高。注意下面两个表达式的区别:
a=b,c;
a=(b,c);


4.

//请问下面的程序一共输出多少个“-”?8个。
int main()
{
	int i;
	for(i = 0;i<2;i++)
	{
		fork();
		printf("-");
	}
	return 0;
}
//一共调用了6次printf,但是会输出8个-。因为父进程的输出缓冲也会被子进程复制。
//因为标准输出是行缓冲,程序遇到"\n"、EOF、文件描述符关闭、主动flush或程序退出等,才会把数据刷出缓冲区。


5.

int fun(char str[14])
{
	return sizeof(str);		//考点:此时str是一个char指针。
}


6.

define a 10
void foo();
int main()
{
	printf("%d ",a);
	foo();
	printf("%d",a);
	return 0;
}
void foo()
{
	#undef a		//在这之前a都被替换成10
	#define a 50		//从这开始a被替换成50
}
//输出10 10


#define a 10
void foo();
void prin();
int main()
{
	prin();
	printf("%d ",a);
	foo();
	printf("%d\n",a);
	return 0;
}
void foo()
{
	#undef a
	#define a 50
}
void prin()
{
	printf("%d ",a);
}
//输出50 10 10,可以看出define只是在预处理阶段将a替换为相应数值,具体替换的值只与define在文件中的位置有关,与是否在函数内无关。


7.

关于浮点数相等问题:通过比较两数相减的绝对值是否小于FLT_EPSILON/DBL_EPSILON/LDBL_EPSILON。


8.

int main()
{
	int a=-3;
	unsigned int b=2;
	long c=a+b;
	printf("%ld\n",c);
}
//c的结果为-1或4294967295。(跟long是32位还是64位有关)

9.

class A
{
public:
	virtual void func(int val=1)
	{
		std::cout<<"A->"<<val<<std:endl;
	}
	virtual void test()
	{
		func();
	}
};
class B : public A
{
public:
	void func(int val=0)
	{
		std::cout<<"B->"<<val<<std::endl;
	}
};
int main()
{
	A *p1=new B;
	B *p2=new B;
	p1->func();	//B->1
	p1->text();	//B->1
	p2->func();	//B->0
	<strong>p2->text();	//B->1(虽然p2的静态类型是B*,但是调用text()后,用A*调用func())</strong>
	return 0;
}


重点:缺省参数值是静态绑定的,绝不重新定义继承而来的缺省参数值。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值