提示:接上文
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
提示:接上文
12月9号
来源:
牛客网公司真题_免费模拟题库_企业面试|笔试真题
1、题目1
在C++程序中,如有如下语句:
int b = 5;
int a = 10;
int &i = b;
int *j = &a;
int *&k = j;
请问下列哪个操作不能实现把a赋值为5。答案B。
A、a = i;
B、k = &b;
C、*k = i;
D、*j = *&b;
解释:答案作对了,但是我当时对int *&k = j;是很懵逼的,看了牛客评论区,感觉有个知识盲区,就是【int &a=b这种a是指针常量】
int *&k = j; //这里也是个声明:代表k 是 j 的另一个别名,可以把 j 和 k 等价(类型是int*)
2、题目2
64 位电脑运行 C++ 结果输出(C)
#include <iostream>
using namespace std;
class A {
char a[2];
public:
virtual void aa() {};
};
class B : public virtual A {
char b[2];
char a[2];
public:
virtual void bb() {};
virtual void aa() {};
};
class C : public virtual B {
char a[2];
char b[2];
char c[2];
public:
virtual void cc() {};
virtual void aa() {};
virtual void bb() {};
};
int main() {
cout << sizeof(A) << endl << sizeof(B) << endl << sizeof(C);
return 0;
}
A、8 16 24
B、16 32 36
C、16 32 48
D、8 20 24
解释:
对于对象 A,包含一个虚函数指针,因此对齐到 8 字节,然后有 2 个 char 型,最后补齐到 8 字节整倍数,因此其长度为 16 。
对于对象 B,包含一个虚函数指针,也是对齐到 8 字节,然后是 4 个 char 型,最后补齐到 8 字节整倍数,变成 16 字节。然后加上对象 A 的大小,共计 32 字节。
对于对象 C,包含一个虚函数指针,也是对齐到 8 字节,然后是 6 个 char 型,最后补齐到 8 字节整倍数,变成 16 字节。然后加上对象 B 的大小,共计 48 字节。
GCC共享虚函数表指针,也就是说父类如果已经有虚函数表指针,那么子类***享父类的虚函数表指针空间,不在占用额外的空间,这一点与VC不同,VC在虚继承情况下,不共享父类虚函数表指针
这个题目我发现其实我并没有完全理解啊,当我想验证不加virtual继承的时候,突然发现没理解
验证代码
#include <iostream>
using namespace std;
class A {
char a[2];
public:
virtual void aa() {};
};
class B : public virtual A {
char b[2];
char a[2];
public:
virtual void bb() {};
virtual void aa() {};
};
class C : public virtual B {
char a[2];
char b[2];
char c[2];
public:
virtual void cc() {};
virtual void aa() {};
virtual void bb() {};
};
int main() {
cout << sizeof(A) << endl << sizeof(B) << endl << sizeof(C);
return 0;
}
//8
//16
//28
这边并没有出现8-16-24是因为sizeof(c) = sizeof(a) + sizeof(b) + 3*sizeof(char) * 2 + 4,加上对齐,就是sizeof(c) = sizeof(a) + sizeof(b) + 8+4 = 28。
不加virtual结果是这样的
#include <iostream>
using namespace std;
class A {
char a[2];
public:
virtual void aa() {};
};
class B : public A {
char b[2];
char a[2];
public:
virtual void bb() {};
virtual void aa() {};
};
class C : public B {
char a[2];
char b[2];
char c[2];
public:
virtual void cc() {};
virtual void aa() {};
virtual void bb() {};
};
int main() {
cout << sizeof(A) << endl << sizeof(B) << endl << sizeof(C);
return 0;
}
//8
//12
//16
这边又不不懂了,评论区有人评论: 普通继承的时候共享,虚继承的时候不共享
这边还要仔细了解下啊
3、题目3
下面程序的执行结果:答案E
class A{
public:
long a;
};
class B : public A {
public:
long b;
};
void seta(A* data, int idx) {
data[idx].a = 2;
}
int main(int argc, char *argv[]) {
B data[4];
for(int i=0; i<4; ++i){
data[i].a = 1;
data[i].b = 1;
seta(data, i);
}
for(int i=0; i<4; ++i){
std::cout << data[i].a << data[i].b;
}
return 0;
}
A、11111111
B、12121212
C、11112222
D、21212121
E、22221111
3、题目3
下面程序的执行结果:答案E
class A{
public:
long a;
};
class B : public A {
public:
long b;
};
void seta(A* data, int idx) {
data[idx].a = 2;
}
int main(int argc, char *argv[]) {
B data[4];
for(int i=0; i<4; ++i){
data[i].a = 1;
data[i].b = 1;
seta(data, i);
}
for(int i=0; i<4; ++i){
std::cout << data[i].a << data[i].b;
}
return 0;
}
A、11111111
B、12121212
C、11112222
D、21212121
E、22221111
解释:
首先明确, A类 大小为 4字节;B 类大小为 8字节
因为B继承自A,基类A有一个long,4字节,派生类B继承A的long加上自身定义了一个long,4+4=8个字节。
所以,A类指针+1移动4字节,B类指针+1移动8字节,所以A类指针和B类指针的移动步长不相同
void seta(A* data, int idx) { data[idx].a = 2; }
由代码可知,此处传入的实参为B类,而形参却为A类,所以这里就是使用基类A类指针来操作,子类B类的数据。
因为A类和B类指针步长不同的原因,就会出现指针实际操作的目标地址,与想象中的目标地址不相同
下面展示,此例题中,因为指针步长不同的原因,所对应的操作地址
因此每执行一次此函数,就会进行4字节对应地址的数据替换。
所以答案就为22221111
因此类推,若将此函数void seta(A* data, int idx);中的A类改为B类,就不会存在指针步长不同的问题,答案就会是21212121
个人感想,这个题目还涉及到有个点就是实参和形参是基类和子类的时候,指向的数据内存其实是实现的内存空间,后面这个点加以注意。这个好像经常出现的情况,比如基类指针指向子类对象或者基类对象,情况是不用的。
12月10号
牛客网公司真题_免费模拟题库_企业面试|笔试真题
1、题目1
通过基类对象名、指针只能使用从基类继承的成员。答案A
A、T
B、F
解释:这个题目就涉及到一个问题,基类指针、子类指针、基类对象、子类对象等问题。
我来整理一下吧,
就是基类指针可以指向基类和子类对象。但是基类指针指向子类对象不能访问子类的新的成员。子类指针指向基类对象有风险。
基类指针指向子类对象需要使用虚函数virtual实现多态,即调用子类的重写的成员函数。基类指针指向基类虚函数,还是调用基类虚函数。即基类指针调用实际指向的对象中的虚函数。
基类指针只能访问基类的重定义函数。子类对象只能访问子类的重定义函数。
传参时,比如子类传给基类,这边搞不清了。不过上述都对应题目和解答,再回顾一下就可以。
看上述的解答,子类传参赋给基类,还是调用子类的虚函数。调用不是虚函数则调用基类的成员函数。基类传给基类不用说了。基类传给子类有风险。
下面代码是验证上面的表述
class A
{
public:
A()
{
}
virtual ~A()
{
}
void funWithoutVirtual()
{
std::cout << "it is A funWithoutVirtual()" << endl;
}
virtual func()
{
std::cout << "it is A func()" << endl;
}
int funcA()
{
std::cout << "it is A funcA()" << endl;
}
};
class B : public A
{
public:
B()
{
}
virtual ~B()
{
}
void funWithoutVirtual()
{
std::cout << "it is B funWithoutVirtual()" << endl;
}
virtual func()
{
std::cout << "it is B func()" << endl;
}
int funcB()
{
std::cout << "it is B funcB()" << endl;
}
};
void testFunc(A* a)
{
a->func(); //看下子类传给基类是不是正常调用子类虚函数,验证是的
//a->funcB(); //error: 'class A' has no member named 'funcB'
}
int main()
{
A* a = new A;
a->func();
//a->funB();
B b;
b.funcA(); //验证子类对象可以基类成员,可以
A* a2 = new B;
a2->func(); //看下基类指针指向子类对象是否正常调用子类虚函数,验证是的
testFunc(&b);
return 0;
}
12月11号
牛客网公司真题_免费模拟题库_企业面试|笔试真题
1、题目1
在64位系统中,有如下类:
class C
{
public:
char a;
static char b;
void *p;
static int *c;
virtual void func1();
virtual void func2();
};
那么sizeof(C)的数值是(D)
A、9
B、17
C、32
D、24
解释:参考答案:应该是D sizeof(类)计算的是类中存在栈中的变量的大小,而类中的b和*c都是static静态变量,存在全局区中,因此不在计算范围之内,于是只剩下char a,void *p和两个virtual虚函数,a是char类型,占用一个字节,p是指针,在64位系统的指针占用8个字节,而两个虚函数只需要一个虚函数表指针,也是八个字节,加上类中的对齐方式(char a对齐时后面补上7个字节),故答案为24.
评论区一个评论:
1 sizeof是用来计算栈大小,不涉及全局区,故类的静态成员大小sizeof不涉及。
2 本题中的虚函数属于同一个类,故只需要一个指针指向虚函数表,所以在64位系统中占用8个字节。就算本题有100个虚函数,那么也只占用8个字节。
综上,占用栈空间的成员有:a, p, func1, func2,由于64位对其,故总空间为8+8+8=24字节。
2、题目2
以下程序的打印结果是(A)
#include<iostream>
using namespace std;
void swap_int(int a , int b)
{
int temp = a;
a = b;
b = temp;
}
void swap_str(char*a , char*b)
{
char*temp = a;
a = b;
b = temp;
}
int main(void)
{
int a = 10;
int b = 5;
char*str_a = "hello world";
char*str_b = "world hello";
swap_int(a , b);.
swap_str(str_a , str_b);
printf("%d %d %s %s\n",a,b,str_a,str_b);
return 0;
}
A、10 5 hello world world hello
B、10 5 world hello hello world
C、5 10 hello world world hello
D、5 10 world hello hello world
解释
#include<iostream>
using namespace std;
void swap_int_with_yinyong(int &a , int &b)
{
int temp = a;
a = b;
b = temp;
}
void swap_int_with_pointer(int* a , int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void swap_str(char*a , char*b)
{
char*temp = a;
a = b;
b = temp;
}
void swap_str_with_pointer(char**a , char**b)
{
char*temp = *a;
*a = *b;
*b = temp;
}
int main(void)
{
int a = 10;
int b = 5;
char*str_a = "hello world";
char*str_b = "world hello";
printf("before exchange: %d %d\n",a,b);
swap_int_with_yinyong(a , b);
printf("exchange a b with yinyong:");
printf("%d %d\n",a,b);
printf("\nbefore exchange: %d %d\n",a,b);
swap_int_with_pointer(&a , &b);
printf("exchange a b with pointer:");
printf("%d %d\n",a,b);
printf("\nbefore exchange: %s %s\n",str_a,str_b);
swap_str(str_a , str_b);
printf("exchange str useless:");
printf("%s %s\n",str_a,str_b);
printf("\nbefore exchange: %s %s\n",str_a,str_b);
swap_str_with_pointer(&str_a , &str_b);
printf("exchange str with pointer:");
printf("%s %s\n",str_a,str_b);
return 0;
}
//before exchange: 10 5
//exchange a b with yinyong:5 10
//
//before exchange: 5 10
//exchange a b with pointer:10 5
//
//before exchange: hello world world hello
//exchange str useless:hello world world hello
//
//before exchange: hello world world hello
//exchange str with pointer:world hello hello world
12月14号
牛客网公司真题_免费模拟题库_企业面试|笔试真题
1、题目1
下面有关c++内存分配堆栈说法错误的是?答案D
A、对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制
B、对于栈来讲,生长方向是向下的,也就是向着内存地址减小的方向;对于堆来讲,它的生长方向是向上的,是向着内存地址增加的方向增长。
C、对于堆来讲,频繁的 new/delete 势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题
D、一般来讲在 32 位系统下,堆内存可以达到4G的空间,但是对于栈来讲,一般都是有一定的空间大小的。
解释:我做对了,但是对于D选项还是不太懂。看了解释,32位系统寻址大小是2^32=4,294,967,296Byte(字节)。2^32=4,294,967,296Byte(字节)/1024(KB)=4,194,304KB=4,194,304KB/1024(M)=4096M=4GB;
而且评论区有人这样说:一般0~3G是用户空间,3G~4G是内核空间, 所以堆内存不应该达到4G。
2、题目2
What’s the runtime behavior of below code piece?(单选B)
int *p = (int *)malloc(sizeof(int));
p = NULL;
free(p);
A、Compiling error: free can't be applied on NULL pointer
B、Memory leak happens
C、Dangling pointer is generated
D、May crash when free() is called with NULL input
解释:这题做对了。但是是查了下free空指针后。
free可以作用于空指针,不会报错。
Dangling pointer悬挂指针、空指针。指指针对应的内存空间已经释放了,但是指针依然指向那块内存空间。这种指针指向的内存空间释放后没有置空的指针,就叫Dangling pointer。处理方法,指向内存空间释放后指针置空。
12月15号
牛客网公司真题_免费模拟题库_企业面试|笔试真题
1、题目1
下列说法错误的是(ABCD)
A、派生类不能访问通过私有继承的基类的保护成员
B、多继承的虚基类不能够实例化
C、如果基类没有默认构造函数,派生类就应当声明带形参的构造函数
D、基类的析构函数和虚函数都不能够被继承,需要在派生类中重新实现
解释:我自己选的是C。
这里还有个知识点盲区就是虚基类。
未完待续......
总结
总结牛客C++题目错题。