文章目录
- C/C++笔试练习
- 选择部分
- (1)new和deleted底层原理
- (2)静态数据成员
- (3)运算符重载
- (4)程序分析
- (5)静态数据成员
- (6)只能使用new创建的类
- (7)模版声明
- 编程题
- (8)另类加法
- (9)走方格的方案数
C/C++笔试练习
选择部分
(1)new和deleted底层原理
C++语言中,类ClassA的构造函数和析构函数的执行次数分别为()
ClassA *pclassa=new ClassA[5];
delete pclassa
A. 5,1
B. 1,1
C. 5,5
D. 1,5
答案选:A
对于扩容和释放空间的函数和操作符,C/C++一般提供了这三种:
1.malloc/realloc/realloc/free
最基本的扩容函数,一般扩容以字节为大小的空间。
2.new/delete
C++引入的新的扩容操作符,new T一般是这样实现的:
<1>new调用operator new(size)来申请空间,operator new使用malloc来循环申请空间;
<2>new再申请完空间之后,会调用T的类构造函数初始化空间。
和free类似,delete用来释放new申请的空间:
<1>delete先调用析构函数释放new p指向对象中的资源;
<2>接着调用operator delete释放p所指的空间,最后使用free清理空间。
注意:new和delete只能申请和释放单个元素的空间,如果要创建多个对象,就要使用new[]了。一定要配合使用,否则会有内存泄漏问题。
3.new[]/delete[]
new T[N]:
<1>new[]调用operator new(size)来申请空间,operator new使用malloc来循环申请空间;
<2>new[]再申请完空间之后,会调用N次T的类构造函数初始化空间。
delete[] p;
<1>delete[]先调用N次析构函数释放new p指向的N个对象中的资源;
<2>接着调用operator delete释放p所指的空间,最后使用free清理空间。
所以这个问题的答案选择A,即类ClassA的构造函数和析构函数的执行次数分别为5和1。因为在代码中,使用new ClassA[5]创建了5个ClassA对象,这将调用5次ClassA的构造函数。然后,使用delete pclassa释放了这5个对象,这将调用一次ClassA的析构函数。 因此,类ClassA的构造函数执行了5次,析构函数执行了1次。所以答案选A。但是这样使用不匹配,可能造成内存泄漏。
(2)静态数据成员
下列静态数据成员的特性中,错误的是()
A. 引用静态数据成员时,要在静态数据成员名前加<类名>和作用域符号
B. 说明静态数据成员时前边要加关键字static来修饰
C. 静态数据成员在类体外进行初始化
D. 静态数据成员不是所有对象所共有的
答案选:D
静态成员变量:
所有对象共享,并不属于具体的某个对象,静态成员变量需要在类中声明然后需要在类外单独进行定义,一定不能在构造函数初始化列表的位置初始化。
在类外访问静态成员变量的时候可以使用: 对象.静态成员变量 或 类名::静态成员变量。
// 通过类名直接访问静态成员变量
int MyClass::staticVar;
// 通过对象名访问静态成员变量
MyClass obj;
int var = obj.staticVar;
引用静态数据成员时,需要在静态数据成员名前添加类名和作用域符号。 这是正确的,所以选项A是正确的。
静态数据成员在定义时需要使用static关键字进行修饰。 这是正确的,所以选项B是正确的。
静态数据成员在类体外进行初始化。 这是正确的,所以选项C是正确的。
静态数据成员是所有对象共享的, 它们不属于任何一个特定的对象实例。因此,选项D是错误的。
(3)运算符重载
在重载一个运算符为成员函数时,其参数表中没有任何参数,这说明该运算符是 ( )
A. 无操作数的运算符
B. 二元运算符
C. 前缀一元运算符
D. 后缀一元运算符
答案选:C
重载成员函数,虽然参数中没有任何参数,但是每个非静态成员函数都有一个隐含的this指针。
注意一下这个前缀一元运算符:
Date& operator++() //前置++
Date operator++(int) //后置++
所以当重载一个运算符为成员函数时,如果该运算符的参数表中没有任何参数,那么说明该运算符是一个一元运算符。 此外,如果该运算符是前缀形式的,那么在参数表中应该有一个空的参数列表;如果该运算符是后缀形式的,那么在参数列表中应该有一个非空的参数列表。因此,选项C是正确的答案。
(4)程序分析
#include<iostream>
using namespace std;
class MyClass{
public:
MyClass(int i=0){cout<<1;}
MyClass(const MyClass&x){cout<<2;}
MyClass& operator=(const MyClass&x){cout<<3; return*this;}
~MyClass(){cout<<4;}
};
int main(){
MyClass obj1(1),obj2(2),obj3(obj1);
return 0;
}
运行时的输出结果是
A. 121,444
B. 112,444
C. 11,114,444
D. 11,314,445
E. 11,314,444
答案选:B
在C++中,一个类的构造函数、析构函数以及赋值运算符都可以被重载。这些函数在类的对象进行某些特定操作时会被自动调用。
这个程序中定义了一个类MyClass,并重载了构造函数、拷贝构造函数、拷贝赋值运算符和析构函数。
构造函数:当你创建一个类的对象时,构造函数会被调用。这里的构造函数有两个参数,一个默认参数0,和一个常量引用参数x,用于拷贝构造。
拷贝构造函数:当一个类对象被另一个类对象初始化时,拷贝构造函数会被调用。这个函数有一个常量引用参数x,输出为2。
拷贝赋值运算符:当使用一个类的对象给另一个同类对象赋值时,拷贝赋值运算符会被调用。这个函数有一个常量引用参数x,输出为3,并且返回当前对象的引用。
析构函数:当一个类对象被销毁时,析构函数会被调用。这个函数没有参数,输出为4。
在main函数中,首先创建了两个对象obj1和obj2,分别输出1和2。然后创建了一个新的对象obj3,使用obj1作为初始化参数,这会调用拷贝构造函数,所以输出为11。最后程序结束,所有对象被销毁,所以输出为444。
所以整个程序的输出应该是112444,答案选B。
(5)静态数据成员
下面有关c++静态数据成员,说法正确的是()
A. 不能在类内初始化
B. 不能被类的对象调用
C. 不能受private修饰符的作用
D. 可以直接用类名调用
答案选:D
C++的静态数据成员可以在类内初始化,也可以在类外初始化,因此选项A错误。
静态数据成员是类的所有对象共享的数据,因此可以通过对象调用, 因此选项B错误。
静态数据成员需要在类外定义和初始化,并且只能被类的对象调用,因此选项D正确。
静态数据成员可以受private修饰符的作用,因此选项C错误。
(6)只能使用new创建的类
在C++中,为了让某个类只能通过new来创建(即如果直接创建对象,编译器将报错),应该()
A. 将构造函数设为私有
B. 将析构函数设为私有
C. 将构造函数和析构函数均设为私有
D. 没有办法能做到
答案选:B
如果将构造函数私有,则在创建A类对象的时候,new会<1>申请空间,<2>调用构造函数,但是构造函数是私有的,就无法调用构造函数了, 也就不可以new对象,所以将构造函数私有无法实现。
但是如果将析构函数私有,在创建A类对象的时候,就可以顺利调用构造函数进行初始化了,只是在delete和析构的时候,无法正常析构。
怎么解决无法正常析构问题?给A类增加一个公有方法,即可:
void Release(A* &p)
{
delete p;
}
(7)模版声明
//下列的模板声明中,其中几个是正确的()
1)template
2)template<T1,T2>
3)template<class T1,T2>
4)template<class T1,class T2>
5)template<typename T1,T2>
6)template<typename T1,typename T2>
7)template<class T1,typename T2>
8)<typename T1,class T2>
A. 2
B. 3
C. 4
D. 5
答案选:B
对于C++模板的声明,正确的模板声明应该遵循以下格式:
template <class T1, class T2>
template <class T1, class T2>其中,T1和T2是模板类型参数。在模板参数列表中,class关键字是可选的。
根据上述规则,以下模板声明是正确的:
template 错误,没有类型
template<T1,T2> 错误,缺少类型关键字
template<class T1,T2> 错误,第二个参数缺少类型关键字
template<typename T1,T2> 错误,第二个参数缺少类型关键字
<typename T1,class T2> - 错误,没有模版说明
所以,正确的应该是2,6,7。
编程题
(8)另类加法
另类加法
本题可以通过位运算实现,具体实现如下:
两个数求和,其实就是 求和后当前位的数据+两个数求和的进位
例如:
1 + 2: 00000001 + 00000010求和后当前位的数据: 00000011 ; 求和后的进位数据: 没有进位,则 00000000两者相加,则得到: 00000011 就是3
2 + 2: 00000010 + 00000010求和后当前位的数据: 00000000; 1和1进位后当前为变成0了,求和后进位的数据: 00000100, 两个1求和后进位了相加后得到: 00000100 就是4
求和后当前位的数据:简便的计算方法就是两个数进行异或 00000001 ^ 00000010 -> 00000011
求和后进位的数据:简便的计算方法就是两个数相与后左移一位 (00000010 & 00000010) << 1
class UnusualAdd {
public:
int addAB(int A, int B) {
if (A == 0) return B;
if (B == 0) return A;
int a = A ^ B;//求和后当前位的数据
int b = (A & B) << 1;//求和后进位的数据
return addAB(a, b);//递归两个数进行相加,任意为0时截止
}
};
(9)走方格的方案数
走方格的方案数
本题为求取路径总数的题目,一般可以通过递归求解,对于复杂的问题,可以通过动态规划求解。也可以通过递归解答。
#include<iostream>
using namespace std;
int pathNum(int n, int m)
{
if (n > 1 && m > 1)
return pathNum(n - 1, m) + pathNum(n, m - 1);
else if (((n >= 1) && (m == 1)) || ((n == 1) && (m >= 1)))
return n + m;
else
return 0;
}
int main()
{
int n, m;
while (cin >> n >> m) {
cout << pathNum(n, m) << endl;
}
return 0;
}