牛客网练习题整理

目录

(1)空的对象指针可以调用方法

   (2)  关于模板的描述

 (3)实型字面值常量有两种表示方式:小数形式和指数形式

 (4)取反运算的算法

 (5)auto_ptr的一些讨论

(6)new int【10】与new int【10】()的差别

(7)强制类型转换 (int)与 (int&)

(8)关于打印格式 %15s与%-5s

(9)友元函数与运算符重载

(10) 关于线程安全性的一些说法

(11)循环优化 


 

 

(1)空的对象指针可以调用方法

头文件已经正常包含,以下代码在VS IDE上编译和运行结果是

1

2

3

4

5

6

7

8

9

class A{

    public:

    void test(){printf("test A");}

};

int main(){

    A*pA=NULL;

    pA->test();

return 0;

}

答案是是输出"test A",为什么空指针可以调用调用test()方法

因为对于非虚成员函数,C++这门语言是静态绑定的。这也是C++语言和其它语言Java, Python的一个显著区别。以此下面的语句为例:

1

pA->test();

这语句的意图是:调用对象 pA 的 test 成员函数。如果这句话在Java或Python等动态绑定的语言之中,编译器生成的代码大概是:
找到 pA 的 test 成员函数,调用它。(注意,这里的找到是程序运行的时候才找的,这也是所谓动态绑定的含义:运行时才绑定这个函数名与其对应的实际代码。有些地方也称这种机制为迟绑定,晚绑定。)
但是对于C++。为了保证程序的运行时效率,C++的设计者认为凡是编译时能确定的事情,就不要拖到运行时再查找了。所以C++的编译器看到这句话会这么干:
1:查找 pA 的类型,发现它有一个非虚的成员函数叫 test 。(编译器干的)
2:找到了,在这里生成一个函数调用,直接调A:: test ( pA )。
所以到了运行时,由于 test ()函数里面并没有任何需要解引用 pA 指针的代码,所以真实情况下也不会引发segment fault。这里对成员函数的解析,和查找其对应的代码的工作都是在编译阶段完成而非运行时完成的,这就是所谓的静态绑定,也叫早绑定。
正确理解C++的静态绑定可以理解一些特殊情况下C++的行为。

(2)关于模板的描述

A函数模板必须由编译器根据程序员的调用类型实例化为可执行的函数。

B函数模板的实例化由编译器实现

C类模板的成员函数都是函数模板

D没使用过的成员函数(即函数模板)不会被实例化

 

(3)实型字面值常量有两种表示方式:小数形式和指数形式

小数形式:由最前面的额正负号,数字0-9和小数点组成,不允许有其他符号;

指数形式;包括指数和尾数两个不可缺少的部分,用符号E(e)分割;E(e)左边是尾数,为十进制整数或小数形式的实数,右边为指数,必须为十进制整数,表示乘以10的多少次方,故选项B不正确

(4)取反运算的算法

1

2

3

4

5

6

#include

main(){

    int i=0;

    i=~i;

    printf("%d\n",i);

程序运行后的输出结果是?

-1

对于任意整型数n的按位取反公式为:~n=-(n+1)

(5)auto_ptr的一些讨论

链接:https://www.nowcoder.com/questionTerminal/c753ca042ec24145b782b20f8d0aff19
来源:牛客网
 

1 auto_ptr的意义 

 std::auto_ptr是C++标准库里面的模版类, 属于智能指针

 当系统异常退出的时候避免资源泄漏(内存)。 其他的资源还对应其他的智能指针。  
2 auto_ptr的使用 
std::auto_ptr test(new int(1)); 
test将是一个auto_ptr的对象,使用一个int指针进行初始化。 
test可以象其他指针一样使用,如使用* 使用->但是++不可以使用,以后也许会扩展,其实难对++做越界管理,也许可以放弃一些速度。  
当使用auto_ptr的时候,必须使用显式的类型转化来初始化,如auto_ptr a(new classA) 
而不能使用auto_ptr a = new classA;  
3 auto_ptr所有权的转移 
auto_ptr对所有权有严格的约定,一个auto_ptr只能控制一个指针,不能控制多个,当auto_ptr拥有一个指针的时候就不能在拥有其他的指针了。同时,不同的auto_ptr不能拥有同一个指针。

链接:https://www.nowcoder.com/questionTerminal/c753ca042ec24145b782b20f8d0aff19
来源:牛客网
 

智能指针实质是一个栈对象,而并非指针类型。C++的auto_ptr的作用是动态分配对象以及当对象不再需要时自动执行清理。

使用std::auto_ptr,要#include
(1)尽量不要使用“operator=”(如果使用了,请不要再使用先前对象)。

(2)记住release()函数不会释放对象,仅仅归还所有权。

(3)std::auto_ptr最好不要当成参数传递(读者可以自行写代码确定为什么不能)。

(4)auto_ptr存储的指针应该为NULL或者指向动态分配的内存块。

(5)auto_ptr存储的指针应该指向单一物件(是new出来的,而不是new[]出来的)。auto_ptr不能指向数组,因为auto_ptr在析构的时候只是调用delete,而数组应该要调用delete[]。

(6)使用auto_ptr作为成员变量,以避免资源泄漏。
(7)auto_ptr不能共享所有权,即不要让两个auto_ptr指向同一个对象。
(8)auto_ptr不能作为容器对象,STL容器中的元素经常要支持拷贝,赋值等操作,在这过程中auto_ptr会传递所有权,那么source与sink元素之间就不等价了。

(6)new int【10】与new int【10】()的差别

int *p1 = new int[10]; 

int *p2 = new int[10]();

p1申请的空间里的值是随机值,p2申请的空间里的值已经初始化

于内置类型而言,new仅仅是分配内存,除非后面显示加(),相当于调用它的构造函数,对于自定义类型而言,只要一调用new,那么编译器不仅仅给它分配内存,还调用它的默认构造函数初始化,即使后面没有加()

在C++primer(第5版)中关于new的讨论有:

1、new当个对象

new在自由空间分配内存,但其无法为其分配的对象命名,因次是无名的,分配之后返回一个指向该对象的指针。

1

int *pi = new int; // pi指向一个动态分配的,未初始化的无名对象

此new表达式在自由空间构造一个int类型对象,并返回指向该对象的指针。

 

默认情况下,动态分配的对象是默认初始化的,这意味着内置类型或组合类型的对象的值是无定义的,而类类型对象将用默认构造函数进行初始化

 

2、new(多个对象)数组

new分配的对象,不管单个对象还是多个对象的分配,都是默认初始化。但可以对数组进行值初始化,方法就是:在大小之后添加一对空括号。

1

2

int *pia = new int[10];    // 10个未初始化int

int *pia2 = new int[10](); // 10个值初始化为0的int

 

(7)强制类型转换 (int)与 (int&)

1

2

3

4

5

6

7

    void main()

{

    float a = 1;

    cout << boolalpha << ((int)a == (int &)a);

    float b = 0;

    cout << boolalpha << ((int)b == (int &)b);

}

 

该程序输出结果为

falsetrue

解析

(int&)a:将a的引用强制转换为整型,意思是a所在的内存,本来定义的时候为float类型并初始为1.0f,但现在我要按int类型解释这段内存(也就是说a所在的内存地址中的数据本来是按float型存储表示的,你非要按int型来解释不可)。
1.0f   在内存中的存储为
0   011   1111   1   000   0000   0000   0000   0000   0000.
把他按整型数解释为2^29+2^28+2^27+2^26+2^25+2^24+2^23=1065353216

(int&)a 相当于*(int*)&a ,*(int*)(&a),*((int*)&a)  

(int)a   a在内存中的值转换成int类型   

(8)关于打印格式 %15s与%-5s

void main()

{

printf("s1=│%15s│s2=│%-5s│","chinabeijing","chi");

}

s1=│□□□chinabeijing│s2=│chi□□│

【解释】%15s表示输出占15个空格的位置,并且右对齐,左边多余的位置补空格,因

为“chinabeijing”包含12个字符,所以输出时,左边要补上3个空格,%-5s表示输出占5

个空格的位置,并且左对齐,右边多余的位置补空格,因为”chi”包含3个字符,所以输出

时,右边要补上2个空格。所以正确的答案是D。

(9)有符号数与无符号数

有如下C语言程序段

1

2

short si = -32767;

unsigned short usi = si;

执行上述两条语句后,usi的值为

32769

先把-32767表示成原码形式是(因为补码不是一步可以得到的,先原码再补码)
(-32767)原码=1111 1111 1111 1111
(-32767)补码 =1000 0000 0000 0001
再把这个补码赋给usi,usi会把最高位符号位1也当做数值位
usi=1000 0000 0000 0001=32769

(9)友元函数与运算符重载

https://blog.csdn.net/ayangya/article/details/78901294

Base operator+(Base);
Base operator--(Base);
Base operator&&(Base, Base);
Base operator++(Base,int);

A选项中,operator+有两个参数,重载函数中只声明了一个参数,属于类的成员函数

B选项中,operator--前置运算符没有参数,后置运算符参数应为int型,因此它重载的是前置--友元函数

C选项中,operator&&有两个参数,属于类的友元函数

D选项中,重载的是operator++后置运算符,两个参数,为友元函数

(10) 关于线程安全性的一些说法

线程安全问题都是由全局变量及静态变量引起的
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全
c++标准库里面的string保证是线程安全的

POSIX线程标准要求C标准库中的大多数函数具备线程安全性(错误)

链接:https://www.nowcoder.com/questionTerminal/fc06bc9cad9e4fe08f4ddebd29332794
来源:牛客网

1.局部变量局部使用是安全的

为什么?因为每个thread 都有自己的运行堆栈,而局部变量是生存在堆栈中,大家不干扰。

2.全局原生变量多线程读写是不安全的  , 全局变量是在堆(heap)中。

3.函数静态变量多线程读写也是不安全的。

4.volatile能保证全局整形变量是多线程安全的么?
不能。 volatile仅仅是告诫compiler不要对这个变量作优化,每次都要从memory取数值,而不是从register

5.InterlockedIncrement保证整型变量自增的原子性

写好多线程安全的法宝就是封装,使数据有保护的被访问到
安全性:


局部变量 > 成员变量 > 全局变

(11)循环优化 

循环优化:死代码删除,代码外提,强度削弱,删除归纳变量,复写传播

 

 

你可能感兴趣的:(C++编程练习)