C++进阶

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

1、

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using  namespace  std;
 
struct  SimpleType
{
     double  db;
     char  sz;
     int  n;
};
 
void  main()
{
     SimpleType a;
     int  nSize =  sizeof (a);
     cout<<nSize<<endl;
}
 
//输出:nSize = 16

解析:这里nSize的值并非13,而是16。这设计结构体的字节对齐问题。编译器在为结构体变量分配空间时,保证下一个成员的偏移量应为该成员数据类型长度的整数倍。首先为db 成员分配空间,假设起始偏移位置从0开始,db 成员将占用0,1,2,3,4,5,6,7共8字节。接下来为成员变量sz 分配空间,由于char 类型占用1字节,sz 将占据8的位置,因为当前位置8与1是整除的。最后为n 成员分配空间,该成员为int 类型,占用4字节。当前偏移位置为9,并不是4的整数倍,因此需要空出3字节(9、10、11),n 从12的位置开始分配4字节的空间。这样就导致了实际分配的大小与“理论上”的大小不一致。


2、

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using  namespace  std;
 
void  OutputString( char  data[])
{
     int  iSize =  sizeof (data);
     cout<< "iSize = " <<iSize<<endl;
}
 
void  main()
{
     int  iLen =  sizeof ( "家园" );
     cout<< "'家园'的大小为:" <<iLen<<endl;
     OutputString( "家园" );
}
 
//输出:
//'家园'的大小为:5
//iSize = 4

解析:作为参数传递的数组其实是以指针的形式传递的,所以在使用sizeof获得数组参数的长度时是4,而不是数字长度。


3、

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using  namespace  std;
 
void  main()
{
     int  i = 2;
     cout<<(i = 3 * 5, 2 * 4)<<endl;
     cout<<i<<endl;
 
     int  x = 9, y = 7;
     int  n = x > y ? (x-y):(x+y);     //三目元表达式
     cout<<n<<endl;
}
 
//输出:
//8
//15
//2

解析:因为赋值运算符的优先级比逗号运算符的优先级高,所以在上面的代码中,会先计算赋值表达式的值,也就是i=3*5,然后再计算逗号表达式的值,所以 i 的值为15,而逗号表达式的值却是8.

又因为条件运算符的优先级高于赋值运算符,所以会先计算 x>y 的值,然后执行符合条件的表达式(x-y),最后将结果赋值给n。


4、x=x+1、x+=1、++ x 、x ++哪一个的效率最高?

解答:x=x+1最低,因为它的的执行过程为:

(1)读取右x的地址;

(2)x+1;

(3)读取左x的地址;

(4)将右值传给左边的x(编译器并不认为左右x的地址相同)。

其次,x+=1,执行过程如下:

(1)读取左x的地址;

(2)x+1;

(3)将得到的值传给x(因为x的地址已经读出)。

x ++ 相当于下列代码 :

(1)y = x;
(2)x += 1;
(3)return y;

++ x 的效率最高,其执行过程为:

(1)读取右x的地址;

(2)x自增1。


5、

?
1
2
3
4
5
6
7
8
int  i=1, j=2, k=3, d=4;
cout<<i+++j<<endl;             //输出:3,先(i++),再(+j),++比+优先级高
cout<<(++k)*(++k)<<endl;         //输出:20,先k自加1等于4,再k自加1等于5,然后4*5=20
(d++)*(d++);
cout<<d<<endl;             //输出:6
 
!i && j++;
cout<<i<<endl<<j<<endl;   //输出:2   3,因为!i运算结束后,整个表达式已肯定为假,所以不必再去计算后面的式子


6、if("A" == a) 比 if(a == "A") 更好,因为如果把”==“误写成”=“,编译器就能检查到错误,因为编译器不允许对常量进行赋值。


7、自动转换遵循以下规则:

1)若参与运算量的类型不同,则先转换成同一类型,然后进行运算。

2)转换按数据长度增加的方向进行,以保证精度不降低。如int型和long型运算时,先把int量转成long型后再进行运算。

     a.若两种类型的字节数不同,转换成字节数高的类型

     b.若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型

3)所有的浮点运算都是以双精度进行的,即使仅含float单精度量运算的表达式,也要先转换成double型,再作运算。

4)char型和short型参与运算时,必须先转换成int型。

5)在赋值运算中,赋值号两边量的数据类型不同时,赋值号右边量的类型将转换为左边量的类型。如果右边量的数据类型长度左边长时,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入向前舍入。

 扩展:

       (1). 在表达式中,char 和 short 类型的值,无论有符号还是无符号,都会自动转换成 int 或者 unsigned int(如果 short 的大小和 int 一样,unsigned short 的表示范围就大于 int,在这种情况下,unsigned short 被转换成 unsigned int)。因为它们被转换成表示范围更大的类型,故而把这种转换称为“升级(promotion)”。 

      (2). 按照从高到低的顺序给各种数据类型分等级,依次为:long double, double, float, unsigned long long, long long, unsigned long, long, unsigned int 和 int。这里有一个小小的例外,如果 long 和 int 大小相同,则 unsigned int 的等级应位于 long 之上。char 和 short 并没有出现于这个等级列表,是因为它们应该已经被升级成了 int 或者 unsigned int。 

      (3). 在任何涉及两种数据类型的操作中,它们之间等级较低的类型会被转换成等级较高的类型。 

      (4). 在赋值语句中,= 右边的值在赋予 = 左边的变量之前,首先要将右边的值的数据类型转换成左边变量的类型。也就是说,左边变量是什么数据类型,右边的值就要转换成什么数据类型的值。这个过程可能导致右边的值的类型升级,也可能导致其类型降级(demotion)。所谓“降级”,是指等级较高的类型被转换成等级较低的类型。 

      5. 作为参数传递给函数时,char 和 short 会被转换成 int,float 会被转换成 double。使用函数原型可以避免这种自动升级。


8、交换两个数:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//方法一:
temp = a;
a = b;
b = temp;
 
//方式二:
a = a + b;
b = a - b;
a = a - b;
 
//方式三:(推荐)
a = a ^ b;
b = a ^ b;
a = a ^ b;


9、在C++中调用被C编译器编译后的函数,为什么要加 extern "c"?

答:C++语言支持函数重载,C语言不支持函数重载,函数被C++编译后在库中的名字与C语言的不同。假设某个函数的名称为:void fun(int x, int y),该函数被C编译器编译后在库中的名字为_fun,而C++编译器则会产生像_fun_int_int之类的名字。C++提供了C连接交换指定符号extern ”C“解决名字匹配问题。


10、

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iostream>
#include <cstring>
using  namespace  std;
 
class  A
{
     
};
class  B
{
     char  a, b;
     B();
     ~B();
};
struct  C
{
     
};
struct  D
{
     int  a;
     char  b;
};
struct  E
{
     long  a1;
     short  a2;
};
struct  F
{
     int  a;
     static  int  b;
};
 
int  main()
{
     A *p1=  new  A();
     A p2;
     A *p3;
     char  *ss1 =  "0123456789" ;
     char  ss2[] =  "0123456789" ;
     char  ss3[100] =  "0123456789" ;
     int  ss4[100];
     char  q1[] =  "ABCDEF" ;
     char  q2[] =  "a\n" ;
     char  *q3 =  "a\n" ;
     string x[] = { "aaa" "bbb" "ccc" };
     void  *i;
     char  *str1 = ( char  *) malloc (100);
     void  *str2 = ( void  *) malloc (100);
     
     cout<< "sizeof(p1): " << sizeof (p1)<<endl;
     cout<< "sizeof(p2): " << sizeof (p2)<<endl;
     cout<< "sizeof(p3): " << sizeof (p3)<<endl;
     cout<< "sizeof(ss1): " << sizeof (ss1)<<endl;
     cout<< "sizeof(*ss1): " << sizeof (*ss1)<<endl;
     cout<< "sizeof(ss2): " << sizeof (ss2)<<endl;
     cout<< "sizeof(ss3): " << sizeof (ss3)<<endl;
     cout<< "sizeof(ss4): " << sizeof (ss4)<<endl;
     cout<< "sizeof(q1): " << sizeof (q1)<<endl;
     cout<< "sizeof(q2): " << sizeof (q2)<<endl;
     cout<< "sizeof(q3): " << sizeof (q3)<<endl;
     cout<< "sizeof(x): " << sizeof (x)<<endl;
     cout<< "sizeof(A): " << sizeof (A)<<endl;
     cout<< "sizeof(B): " << sizeof (B)<<endl;
     cout<< "sizeof(C): " << sizeof (C)<<endl;
     cout<< "sizeof(D): " << sizeof (D)<<endl;
     cout<< "sizeof(E): " << sizeof (E)<<endl;
     cout<< "sizeof(F): " << sizeof (F)<<endl;
     cout<< "sizeof(str1): " << sizeof (str1)<<endl;
     cout<< "sizeof(str2): " << sizeof (str2)<<endl;
     cout<< "sizeof(" "): " << sizeof ( "" )<<endl;
     cout<< "sizeof(\"明\"): " << sizeof ( "明" )<<endl;
     cout<< "sizeof(i): " << sizeof (i)<<endl;
}
 
/*输出:
sizeof(p1): 4
sizeof(p2): 1
sizeof(p3): 4
sizeof(ss1): 4
sizeof(*ss1): 1
sizeof(ss2): 11
sizeof(ss3): 100
sizeof(ss4): 400
sizeof(q1): 7
sizeof(q2): 3
sizeof(q3): 4
sizeof(x): 48
sizeof(A): 1
sizeof(B): 2
sizeof(C): 1
sizeof(D): 8
sizeof(E): 8
sizeof(F): 4
sizeof(str1): 4
sizeof(str2): 4
sizeof(): 1
sizeof("明"): 3
sizeof(i): 4
*/

解析:

        

        空类所占空间为1,单一继承的空类也为1,多继承的空类还是1,但虚继承涉及到虚表(虚指针),其所占空间为4.


11、类中的this指针有以下特点:

(1)this只能在成员函数中使用,在全局函数、静态函数中都不能使用this;

(2)this在成员函数的开始前构造,在成员函数结束后清除;


12、C++中有了malloc/free,为什么还要new/delete?

答:malloc和free是C++/C语言的标准库函数,new和delete是C++的运算符。对于非内部数据类型的对象而言,光用malloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于new/delete。因此C++需要一个能够完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的delete。


13、

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
using  namespace  std;
 
class  A
{
public :
     virtual  f()
     {
         cout<< "A" <<endl;
     }
};
 
class  B :  public  A
{
public :
     virtual  f()
     {
         cout<< "B" <<endl;
     }
};
 
void  main()
{
     A* pa =  new  A();
     pa->f();
     B* pb = (B *)pa;
     pb->f();
     delete  pa, pb;
     pa =  new  B();
     pa->f();
     pb = (B *)pa;
     pb->f();
}
 
/*输出:
A
A
B
B
*/


14、

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<iostream>
using  namespace  std;
 
class  A
{
     char  a[3];
     virtual  void  aa() {};
};
  
  class  B :  virtual  public  A
  {
    char  b[3];
    virtual  void  bb() {};
  };
  
  class  C :  public  A
  {
    char  c[3];
    virtual  void  cc() {};
  };
  
int  main()
{
    A a;
    B b;
    C c;
    cout<< sizeof (a)<<endl;
    cout<< sizeof (b)<<endl;
    cout<< sizeof (c)<<endl; 
    return  0;
  }

目录
相关文章
|
5月前
|
编译器 C++
C++进阶之路:何为命名空间、缺省参数与函数重载
C++进阶之路:何为命名空间、缺省参数与函数重载
37 3
|
5月前
|
编译器 C++
C++进阶之路:何为运算符重载、赋值运算符重载与前后置++重载(类与对象_中篇)
C++进阶之路:何为运算符重载、赋值运算符重载与前后置++重载(类与对象_中篇)
46 1
|
5月前
|
存储 编译器 C++
C++进阶之路:何为拷贝构造函数,深入理解浅拷贝与深拷贝(类与对象_中篇)
C++进阶之路:何为拷贝构造函数,深入理解浅拷贝与深拷贝(类与对象_中篇)
49 0
|
5月前
|
安全 算法 C语言
【C++进阶】深入STL之string:掌握高效字符串处理的关键
【C++进阶】深入STL之string:掌握高效字符串处理的关键
56 1
【C++进阶】深入STL之string:掌握高效字符串处理的关键
|
5月前
|
编译器 C++
C++模板进阶
C++模板进阶
24 1
|
5月前
|
存储 算法 程序员
【C++进阶】深入STL之 栈与队列:数据结构探索之旅
【C++进阶】深入STL之 栈与队列:数据结构探索之旅
55 4
|
5月前
|
算法 安全 编译器
【C++进阶】模板进阶与仿函数:C++编程中的泛型与函数式编程思想
【C++进阶】模板进阶与仿函数:C++编程中的泛型与函数式编程思想
49 1
|
5月前
|
存储 算法 程序员
【C++进阶】深入STL之vector:构建高效C++程序的基石
【C++进阶】深入STL之vector:构建高效C++程序的基石
52 1
|
5月前
|
编译器 C++
【C++进阶】深入STL之string:模拟实现走进C++字符串的世界
【C++进阶】深入STL之string:模拟实现走进C++字符串的世界
37 1
|
5月前
|
算法 编译器 C语言
C++进阶之路:深入理解编程范式,从面向过程到面向对象(类与对象_上篇)
C++进阶之路:深入理解编程范式,从面向过程到面向对象(类与对象_上篇)
64 3