首页 > 技术文章 > 牛课-C++基础知识-选择1

GuoXinxin 2019-10-15 15:36 原文

1. 下面程序执行后输出结果为(      )

#include <stdio.h>
int main()
{
int i, j, m=6,n=4,  *p=&n, *q=&m;

i=p==&m;
j=(-*p)/(*q)+7;

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

return 0;
}

  • 编译错误
  • i=0,j=7
  • i=0,j=0
  • 其他几项都不对

正确答案:B

运算符优先级 https://www.cnblogs.com/zhouhappy88/p/7674858.html

==优先级高于=,p==&m为假命题,则i=0;
j=(-*p)/(*q)+7; j=-4/6+7,-4/6=0,则j=7;
因此,答案为i=0,j=7,选B

  

2、下列const使用方法错误的是(      )

  • const int Val = 10; Val = 20;
  • class A   {         const int SIZE = 100;         int array[SIZE];   };
  • class A { protected:  static int const Inity; };
  • int a=7;     const int *aPtr;      aPtr = &amp;a;

正确答案:A

      一、常量指针(constant pointers):指针自身是常量,const关键字出现在星号右边。如:

1 char greeting[] = "Hello";
2 char* const p = greeting; // const pointer, non-const data

 指针本身是常量,指向的地址不可以变化,但是指向的地址所对应的内容可以变化;      

   二、指针常量(pointers to constants):指针所指物是常量,const关键字出现在星号左边。如: 

1 char greeting[] = "Hello";
2 const char* p = greeting;      // non-const pointer, const data


3 char const* p = greeting; // non-const pointer, const data

关键点:

1.常量指针指向的对象不能通过这个指针来修改,可是仍然可以通过原来的声明修改;

2.常量指针可以被赋值为变量的地址,之所以叫常量指针,是限制了通过这个指针修改变量的值;

3.指针还可以指向别处,因为指针本身只是个变量,可以指向任意地址;

3、C++中空类默认产生哪些类成员函数()

  • 默认构造函数
  • 析构函数
  • 拷贝构造函数
  • 赋值函数
正确答案:A B C D

4、一个空类占用多大的空间

用程序来实现一个空类和一个多重继承的空类。看看它们的输出结果:

#include<iostream>
#include<memory.h>
#include<assert.h>

using namespace std;

class A
{};

class A2
{};

class B:public A
{};

class C:public virtual B
{};

class D:public A,public A2
{};

int main()
{
cout<<"sizeof(A):"<<sizeof(A)<<endl;
cout<<"sizeof(B):"<<sizeof(B)<<endl;
cout<<"sizeof(C):"<<sizeof(C)<<endl;
cout<<"sizeof(D):"<<sizeof(D)<<endl;
return 0;
}

这说明空类所占的空间为1单一继承的空类空间也是1多重继承的空类空间也是1.但是虚继承涉及虚表(虚指针),所以sizeof(C)大小为4;  

5、下面代码段执行的输出结果为

1
2
3
4
int a=2,*pa=&a;
int b=3,*pb=&b;
*pa*=*pa**pb;
cout<<a<<endl;

 

 

  • 2
  • 6
  • 12
  • 语法错误
解引用“ * ”的优先级比 乘“*” 高;乘“ * ”的优先级比乘后赋值“ *= ”高。
解引用与乘后赋值的结合性都是从右到左,综上,题干中表达式等价于
*pa *= 2*3;
*pa = (*pa) * 6;
*pa = 12;

6、while的使用

while(-1){
    cout << "Hello World1";
}  

 会一直循环输出

7、printf的返回值

#include<stdio.h>
 
int main()
{
 printf("%d",printf("%d",printf("%d",43)));
 return 0;
}

 输出4321

返回值是输出的长度

首先,第三个printf输出43 。返回输出的长度“2”;

然后,第二个printf输出2。返回输出长度"1";

最后输出1

8、由于数组名就是数组首元素的地址,所以p[i]=*(p+i)

9、关于构造函数

1. 构造函数不可以是私有的(private)

错误

2. 一个类中可以有多个构造函数(对,构造函数可以重载,传入参数、不传入参数、传入不同的参数)

3. 无论何时,只要类的对象被创建,就会执行构造函数(对)

4. 构造函数没有返回类型(对)

单例模式:构造函数可以是私有的(private)

个人理解:把构造函数私有化(private)构造函数就不能通过外部访问,构造函数不能访问,自然就不能通过new创建一个新对象。(new对象时,一定会初始化,即调用构造函数)

作用:在单例设计中,Singleton类在定义的时候将构造方法私有化,而在内部仅仅new出一个对象,是为了禁止别的类在外面直接new Singleton()出来,这样你只要在Singleton类中new一个对象,就能确保无论什么情况都只会产生一个Singleton对象,外部无法new出来,内部已经定义好了,有且仅有一个对象,这就是单列设计的核心。单例设计模式目标是保证一个类在内存中对象的唯一性。所以要保证自己的类中创建了对象之后,其他的类不能再创建对象,只能获取这个对象。所以只要将本类中的构造函数私有化,其他程序就无法再创建该类对象。

 


 

1.构造函数

  作用:初始化对象的数据成员,无论何时只要类的对象被创建就会执行构造函数
  特点:构造函数名字和类名字相同;且没有返回值类型;可以含有参数列表(可能没有参数)和函数体(可能是空);构造函数不能申明为const如果创建const对象,直到构造函数完成初始化过程,对象才取的常量属性

2.构造函数的类型:默认构造函数,显式定义的构造函数

   默认构造函数:默认的构造函数没有任何参数,默认构造函数分为合成的默认构造函数与自定义的默认构造函数
   合成的默认构造函数:如果我们的类没有显式的定义构造函数,编译器会隐式的定义一个默认构造函数,称为合成的默认构造函数,一旦我们定义了其他的构造函数,编译器将不会为我们定义合成的默认构造函数

合成的默认构造函数按照如下规则初始化数据成员:

    1.如果存在类内初始值,用它来初始化成员;

     2.否则默认初始化
 

    自定义的默认构造函数:例如Sales_data()=default,自定构造函数的作用完全等价于合成默认构造函数;

需要自定义默认构造函数的原因

     1.编译器只有在发现类不包含任何其他的构造函数时才替我们定义合成的默认构造函数,一旦我们定义了其他的构造函数,除非我们定义自定义的默认构造函数,否则类将没有默认构造函数
      2.合成的默认构造函数可能执行错误的操作,因为合成的默认构造函数初始化的内置类型成员(没有类内初始值时)时,可能得到未定义的值
      3.有的编译器不能为类定义合成的默认构造函数,例如:类中包含一个其他类类型的成员且这个成员没有默认构造函数,则编译器将无法初始化该成员
注意:如果累包含内置类型或复合类型成员,则只有这些成员全部都赋予了类内初始值时,这个类才适合使用合成的默认构造函数

更详细的理解:https://www.cnblogs.com/downey-blog/p/10470782.html

10、分析下面代码有什么问题?

void test1()
{
 char string[10];
 char* str1 = "0123456789";
 strcpy( string, str1 );
}

代码本身没有错误(可以运行),如果将str1拷贝到string中,string的长度会增加,并且也可以正确的输出string。

(c、java代码都测试过,没有问题)
但是存在潜在的危险,string定义的长度是10,而拷入str1之后string的长度增为11,也就是说,在内存中,如果紧接string之后有内容,将被覆盖,这样会导致string之后的内存存取错误

11、warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]

在C++中,

char* p = "abc";  // valid in C, invalid in C++

会跳出警告:warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]

改成下面会通过warning

char* p = (char*)"abc";  // OK

或者改成下面:

char const *p = "abc";  // OK

原因解析:

我们在学习c或者c++的时候都知道,如果在赋值操作的时候,等号两边的变量类型不一样,那么编译器会进行一种叫做 implicit conversion 的操作来使得变量可以被赋值。

在我们上面的表达式中就存在这样的一个问题,等号右边的"abc"是一个不变常量,在c++中叫做string literal,type是const char *,而p则是一个char指针。如果强行赋值会发生什么呢?没错,就是将右边的常量强制类型转换成一个指针,结果就是我们在修改一个const常量。编译运行的结果会因编译器和操作系统共同决定,有的编译器会通过,有的会抛异常,就算过了也可能因为操作系统的敏感性而被杀掉。

像这种直接将string literal 赋值给指针的操作被开发者们认为是deprecated,只不过由于以前很多代码都有这种习惯,为了兼容,就保留下来了。

12、对于以下代码,描述正确的是:

list = ['1', '2', '3', '4', '5']
print list[10:]

  • 导致 IndexError
  • 输出['1', '2', '3', '4', '5']
  • 编译错误
  • 输出[]

正确答案:D  

13、有以下语句定义

1
2
3
int a[2][3];
int (*p)[3]=a; 
int *q=*a;

 
 
 
则能输出a[1][2]的值的语句是(      )

  • cout<<*(*(a+1)+2);
  • cout<<p[1][2];
  • cout<<*(q+5);
  • cout<<q[1][2];

正确答案:A B C  

*(*(a+1)+2)第一行第二列即a[1][2]                                                                      -----A
相当于p指向a,然后取p[1][2]<==>a[1][2]                                                          -----B
*(q+5)第五个选项值为5                                                                                      -----C
编译就错了,a本身就是数组的首地址,在使用解引符赋值给指针q编译不过    -----D

14、若有定义语句: char c='\n'; 则变量 c ( )

  • 含4个字符
  • 含3个字符
  • 含2个字符
  • 含1个字符

正确答案 D

char单个字符;
char*字符串,包含字符串结束符

15、extern "c"的作用有哪些()

  • 实现C++代码调用其他C语言代码
  • 使C函数不被C++编译器优化
  • 使C函数使用C编译器优化
  • 到出C变量或函数

 正确答案:A

extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言(而不是C++)的方式进行编译。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。

例如函数void fun(int, int),编译后的可能是_fun_int_int(不同编译器可能不同,但都采用了类似的机制,用函数名和参数类型来命名编译后的函数名);而C语言没有类似的重载机制,一般是利用函数名来指明编译后的函数名的,对应上面的函数可能会是_fun这样的名字。

extern "C"包含双重含义,从字面上可以知道,首先,被它修饰的目标是"extern"的;其次,被它修饰的目标代码是"C"的。

  • 被extern "C"限定的函数或变量是extern类型的

extern是C/C++语言中表明函数和全局变量的作用范围的关键字,该关键字告诉编译器,其申明的函数和变量可以在本模块或其他模块中使用

记住,语句:extern int a; 仅仅是一个变量的声明,其并不是在定义变量a,也并未为a分配空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出错。

通常来说在模块的头文件中对本模块提供给其他模块引用的函数和全局变量以关键字extern生命例如,如果模块B要引用模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但并不会报错;它会在链接阶段从模块A编译生成的目标代码中找到该函数。

extern对应的关键字是static,static表明变量或者函数只能在本模块中使用,因此,被static修饰的变量或者函数不可能被extern C修饰

  • 被extern "C"修饰的变量和函数是按照C语言方式进行编译和链接的:这点很重要!!!!
  • 不可以将extern "C" 添加在函数内部
  • 如果函数有多个声明,可以都加extern "C", 也可以只出现在第一次声明中,后面的声明会接受第一个链接指示符的规则。
  • 除extern "C", 还有extern "FORTRAN" 等。

16、下列关于类中的静态成员的说法错误的是()

  • 虽然静态成员不属于类的某个对象,但是我们仍然可以使用类的对象、引用或者指针来访问静态成员
  • 成员函数不用通过作用域运算符就能直接使用静态成员
  • 静态数据成员不是由类的构造函数初始化的
  • 静态成员不可以作为默认实参

 正确答案:D 

静态成员与普通成员无异,除了其周期跟初始(系统给初值)                                  -----A

c++基本姿势,类内使用不用域名符                                                                      -----B

静态成员变量类外定义,系统有给初值                                                                 -----C
(类内)非静态成员不可作为默认实参,静态成员在函数解析前就存在,但非静态则相反 -----D

因为静态变量在类初始化前就已经初始化好了,对类的对象来说就像一个常数一样,所以可以做默认参数,而类内的变量在类的实现初始化时才初始化,这样无法确定默认参数的值。所以不行。

17、引用

把函数不会改变的形参定义成普通的引用是一种比较常见的错误,这么做会带给函数调用者一种误导,即函数可以修改形参的值。

此外,使用引用而非常量引用也会极大的限制函数所能接受的实参类型。我们不能把const对象,字面值或者需要类型转换的对象传递给普通的引用形参。因此应尽量使用常量引用形参。

#include <iostream>
#include <string>
#include <cctype>
using namespace std;
 
void to_upper(string &s){   //普通的引用
    for(auto &i : s)
        i = toupper(i);
}
 
int main()
{
    string s = "i miss you";
    to_upper("abcdefg");  //错误,不能将字面值常量传给非常量引用
    to_upper(s);       //正确
    cout<<s<<endl;
    return 0;
}

18、C++ 顶层const与底层const的区别

底层const是代表对象本身是一个常量(不可改变);

顶层const是代表指针的值是一个常量,而指针的值(即对象的地址)的内容可以改变(指向的不可改变);

 1 #include <iostream>
 2 
 3 int main()
 4 {
 5     int i=0;
 6     int* const p1=&i;   //-----不能改变p1的值,这是一个顶层const
 7     const int b=i;      //-----不能改变b的值,这是一个顶层const(一般变量前const都是顶层const)
 8     const int* p2=&b;   //-----可以改变p2的值,这是一个底层const(对象是个const,所以可以底层指向顶层)
 9     const int*const p3=p2; //--靠右边的是顶层const,靠左的事底层const;
10     const int& some=i;   //--- 用于声明引用的,都是底层const;
11 }

当执行对象的拷贝操作时,常量时顶层const还是底层const时就区别明显,其中顶层const不受什么影响;

底层const执行拷贝是2对象的类型必须一样,或者可转换,一般非常量可以转换成常量;

 19、定义宏#define DECLARE(name, type) type name##_##type##_type,

则DECLARE(val, int)替换结果为()

  • int val_int_type
  • int val_int_int
  • int name_int_int
  • int name_int_name

正确答案:A

##是一种分隔连接方式,它的作用是先分隔,然后进行强制连接

“name”和第一个“_”之间被分隔了,所以预处理器会把name##_##type##_type解释成4段:“name”、“_”、“type”以及“_type”,name和type会被替换,而_type不会被替换

 c++宏详细知识,见:https://www.cnblogs.com/GuoXinxin/p/11684337.html

 

 

  

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

 

推荐阅读