C++笔试

1.交换字符串
void SwapString(char *&x,char *&y)
{
	char *temp=x;
	x=y;
	y=temp;
}
调用方式:
SwapString(a,b);

char *&x表示指针的引用,即char *表示字符串,&表示按引用传值。

或者

void SwapString(char **x,char **y)
{
	char *temp=*x;
	*x=*y;
	*y=temp;
}
调用方式:
SwapString(&a,&b);


3.指针加减操作
#include <iostream>
using namespace std;


int main()
{
	int a[5]={1,2,3,4,5};
	int *ptr=(int *)(&a+1);
	cout<<*(a+1)<<endl;
	cout<<*(ptr-1)<<endl;
	return 0;
}

输出:2 5


4.指针比较
#include <iostream>
using namespace std;


int main()
{
	char str1[]="abc";   //str1在栈取
	char str2[]="abc";   //str2在栈区,但是str1与str2在栈区不同位置,不相等
	const char str3[]="abc";  //str3在栈区
	const char str4[]="abc";  //str4在栈区,但是str3与str4在栈区不同位置,不相等
	const char *str5="abc";    //"abc"在文字常量区,str5是"abc"的地址
	const char *str6="abc";    //str6也是"abc"的地址,和str5相等
	char *str7="abc";    //str7也是"abc"的地址
	char *str8="abc";    //str8也是"abc"的地址
	cout<<(str1==str2)<<endl;  //0
	cout<<(str3==str4)<<endl;  //0
	cout<<(str5==str6)<<endl;  //1
	cout<<(str6==str7)<<endl;  //1
	cout<<(str7==str8)<<endl;  //1
	return 0;
}


5.构造函数调用虚函数

#include<iostream>
#include <vector>
#include <assert.h>
#include <string>
using namespace std;


class Base{
public:
	Base(){ doSth(); }   //构造函数调用虚函数
	virtual void doSth(){
		cout << "I am Base" << endl;
	}
};

class Derived :public Base{
public:
	virtual void doSth(){
		cout << "I am Derived" << endl;
	}
};

int main(void)
{
	Derived d; //输出:I am Base
	return(0);
}
基类的构造函数在派生类构造函数之前执行,当基类构造函数运行时,派生类数据成员还没有被初始化。在构造函数中,虚函数机制不会发生作用。


6.如果继承类调用基类的非virtual函数,而该函数里面调用virtual函数,则在调用该virtual函数时,依然会出现多态。

#include<iostream>
#include <vector>
#include <assert.h>
#include <string>
using namespace std;


class Base{
public:
	Base(){}   //构造函数调用虚函数
	void f(){ doSth(); }
	virtual void doSth(){
		cout << "I am Base" << endl;
	}
};

class Derived :public Base{
public:
	virtual void doSth(){
		cout << "I am Derived" << endl;
	}
};

int main(void)
{
	Base *b=new Derived; 
	b->f();   //输出:I am Derived
	return(0);
}

7.多重继承构造顺序

class Parent{
public:
	Parent(int n) :num(n){ cout << num << " Parent(int)" << endl; }
private:
	int num;
};

class Child1 :virtual public Parent{
public:
	Child1(int num) :Parent(num){ cout << num << "Child1(int)" << endl; }
};

class Child2 :virtual public Parent{
public:
	Child2(int num) :Parent(num){ cout << num << "Child2(int)" << endl; }
};

class Derived :public Child1, public Child2{
public:
	Derived(int num) :Child1(num), Child2(num + 1){}  //错误:Parent没有默认构造函数(Parent的初始化是按照Derived调用的构造函数来的)
};

8.构造函数调用另一个构造函数

#include<iostream>  
#include <vector>  
#include <assert.h>  
#include <string>  
using namespace std;  
  
  
struct CLS{  
    int m_i;  
    CLS(int i) :m_i(i){  
        cout << "CLS(int):this =" << this << endl;  
    }  
    CLS()  
    {  
        CLS(0);  //生成一个临时对象,对调用该函数的对象本身没影响  
        cout << "CLS():this =" << this << endl;  
    }  
};  
int main(void)  
{  
    CLS obj;  
    cout << "&obj=" << &obj << endl;  
    cout << obj.m_i << endl;  
    return(0);  
}

9.父类没有默认构造函数时,子类需要显式调用父类构造函数。

#include <iostream> 
#include <string>
using namespace std;

class Person{
private:
	string name;
public:
	Person(string nm){
		name = nm;
	}
};

class Employee :public Person{
	string empID;
public:
	Employee(string id){
		empID = id;
	}
	string GetEmpId(){ return empID; }
};

int main()
{
	Employee *e = new Employee("123");
	cout << e->GetEmpId() << endl;
	return 0;
}

这段代码会编译错误,修改如下:

#include <iostream> 
#include <string>
using namespace std;

class Person{
private:
	string name;
public:
	Person(string nm){
		name = nm;
	}
};

class Employee :public Person{
	string empID;
public:
	Employee(string id):Person("No name"){ //显式调用
		empID = id;
	}
	string GetEmpId(){ return empID; }
};

int main()
{
	Employee *e = new Employee("123");
	cout << e->GetEmpId() << endl;
	return 0;
}


10.在析构函数里面delete this指针.

如果一个类的析构函数如下:

class MyClass{
public:
	~MyClass(){
		delete this;
		this = NULL;
	}
};
则会出现编译错误,因为this指针本身是const不允许进行修改。

如果去掉对this赋值这句,则会导致栈溢出。因为delete的过程实际是先调用析构函数,然后释放内存,在析构函数里调用delete会导致递归调用,从而导致栈溢出。
如果在一般成员函数中删除this指针会出现什么情况?

#include <iostream> 
#include <string>
using namespace std;

class MyClass{
public:
	void Delete(){ cout << "Delete" << endl; delete this; }
	void Display(){ cout << "Display" << endl; }
	~MyClass(){
	}
};

int main()
{
	MyClass *my=new MyClass;
	my->Delete();
	my->Display();
	return 0;
}

在上面的例子中,可以正常的输出。

如果this对象不是new出来的呢?

#include <iostream> 
#include <string>
using namespace std;

class MyClass{
public:
	void Delete(){ cout << "Delete" << endl; delete this; }
	void Display(){ cout << "Display" << endl; }
	~MyClass(){
	}
};

int main()
{
	MyClass my;
	my.Delete();
	my.Display();
	return 0;
}
改程序在delete this:那句会崩溃,原因是this对象是在栈空间分配的,不能用delete来释放。
至于上面的例子,在delete this指针后,还能调用my->Display();原因是delete操作先调用析构函数,再调用delete运算符释放this指向的内存。delete之后不能再访问成员变量以及虚函数了。因为成员变量和vptr是存放在this指向的内存的。

11.关于char data[0]。

#include <iostream> 
#include <string>
using namespace std;

struct Node{
	int size;
	char data[0];
};

int main()
{
	char str[10] = "123456789";
	Node *n = (Node *)malloc(sizeof(Node) + strlen(str));
	memcpy(n->data, str, 10);
	cout << sizeof(Node) << endl;
	cout << n->data << endl;
	return 0;
}
编译器会认为data是一个长度为0的数组,而且会支持对data的跨界访问。

12.关于static局部变量

#include <iostream>
#include <string>
#include <vector>
using namespace std;


void func()
{
	static int i = 0;
	i++;
	cout << i << endl;
}

int main()
{
	func();
	func();
	func();
	return 0;
}
//输出:
//1
//2
//3


13.类对象指针指向NULL,仍可以通过该指针调用成员函数

#include<iostream>  
#include <vector>
#include <stack>
using namespace std;

class A{
public:
	void Test(){ cout << "Test" << endl; }
};

int main(){
	A *a = NULL;
	a->Test();
	return 0;
}
上面代码可以正常输出:Test

原因:

对于成员函数,并不是每个对象都拥有一份成员函数体而是所有的对象共用一份成员函数,程序编译之后,成员函数地址就已经确定。

而成员函数之所以能把属于此类的各个对象的数据区别开,就是靠这个this指针。函数体内所有对类数据成员的访问,都会被转化为this->数据成员的方式。

注意:静态成员函数也可以这样调用。

14.const修饰指针和指针所指对象

const在*号之前,修饰的是指针所指的对象,如const int * p,表示p所指的对象是常量,等价于int const * p。

const在*号之后,修饰的是指针本身,如int * const p,表示p表示的地址是常量,一般定义的时候要初始化。



15.函数指针和数组指针、指针数组

int *p[3]:指针数组,数组的每一个元素都是一个int型指针。

int (*p)[3]:数组指针,指针指向一个拥有3个int型数据的数组。

int (**p)[3]:是一个二级指针,指向的是一个一位数组的指针。(指向指针的指针)

int *(*p)[3]:数组指针,指向一个拥有3个int *类型数据的数组。

int (*p[3])():p是一个数组,数组有3个元素,元素都是指向函数的指针,指向的函数是没有参数且返回int的函数。

int *((*p)[10]):和nt *(*p)[3]一样

int (*p)(int):p是指针,指向一个函数,函数的参数是int型且返回int型。

int (*(*p)(int,int))(int):p是一个函数指针,指向的函数有两个int参数并且返回一个函数指针,返回的函数指针指向有一个int参数且返回int的函数。

int (*(*p)[3])(int *):p是数组指针,指向一个拥有3个元素的数组,每个元素都是一个函数指针,指向的函数参数是int *,返回类型是int。



16.在类的成员函数中调用delete this

myClass::foo(){
	delete this;
}
..
void func(){
	myClass *a = new myClass();
	a->foo();
}
在delete this之后的其他任何函数调用,只要不涉及this指针涉及的内容(数据成员和虚函数表指针),都能正常运行。所以这段代码没有问题。

注意:在析构函数中调用delete this,会导致堆栈溢出。因为delete的本质是为将要释放的内存调用一个或多个析构函数。delete this会调用本对象的析构函数,最终造成无线循环。

注意:this指针类型是 MyClass *const this,不能进行修改,如果在delete this后面加上this=NULL,则会导致编译不通过。


17.关于抽象类

1)抽象类只能用作其他类的基类,不能定义抽象类的对象。

2)抽象类不能用于参数类型、函数返回值或显示转换的类型

3)抽象类可以定义抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性。


18.关于多态

多态性包括两种:第一种是静态多态,通过函数重载和类模板实现;

第二种是动态多态,通过继承中的多态实现。


19.不能被重载的运算符

?:    ::     .    .*    sizeof    typeid       static_cast       dynamic_cast       interpret_cast


20.vector的两种访问方法

1)vector::at():进行边界检查,如果访问超过了vector的范围,将抛出一个例外。v.at(i)

2)vector::operator[]:不进行边界检查,与C进行兼容。


21.STL一级容器:指容器元素本身是基本类型,非组合类型,有vector, list, deque。


22.关于几种排序

不稳定排序:选快希堆不稳

与初始顺序无关(时间复杂度不变):对归选基不变



23.在重载一个运算符为成员函数时,其参数表中没有任何参数,说明该运算符是前缀一元运算符。

后缀一元运算符会加一个int参数,用来区分前后缀。



24.通过指针改变const修饰的变量

#include <iostream> 
using namespace std; 
int main(void) 
{ 
    const int a = 10; 
    int * p = (int *)(&a); 
    *p = 20; 
    cout<<"a = "<<a<<", *p = "<<*p<<endl; 
    return 0; 
} 
输出:a = 10, *p = 20
原因是编译器在优化的过程中,会把const全部以内容替换掉,所以上面用10来替换掉a,而打印那个内存单元的地址对应的值就变成了20。


25.关于类的大小

struct A{
	A() {}
	~A() {}
	int m1;
	int m2;
};
struct B :A{
	B() {}
	~B() {}
	int m1;
	char m2;
	static char m3;
};
struct C{
	C() {}
	virtual~C() {}
	int m1;
	short m2;
};
类的大小只与成员变量(非static数据成员变量)和虚函数指针有关,还要考虑到对齐。

sizeof(A)=8字节

sizeof(B)=16字节(在类A的8个字节基础上再加上B自己的8字节)

sizeof(C)=12字节


26.构造和析构顺序

#include <iostream>
using namespace std;

class A
{
public:
	A()  { cout << "create A" << endl; }
	A(const A& other){ cout << "copy A" << endl; } //复制构造函数
	~A() { cout << "~A" << endl; }
};

class B :public A
{
public:
	B(A &a) :_a(a)
	{
		cout << "create B" << endl;
	}
	~B()
	{
		cout << "~B" << endl;
	}
private:
	A _a;
};

int main(void)
{
	A a;       //很简单,定义a的时候调用了一次构造函数  
	B b(a);
}
程序输出为:

create A(对应A a;)

create A(注意:创建b时先调用基类的默认构造函数,并不是对应_a的构造函数。)

copy A(对应A _a;但是使用的是拷贝构造函数,需要注意)

create B(然后才是执行B的构造函数内容)

~B(最后构造的最先析构)

~A

~A

~A


27.函数参数入栈顺序

#include <iostream>
using namespace std;

void func(const int& v1, const int& v2)
{
	std::cout << v1 << ' ';
	std::cout << v2 << ' ';
}

int main(int argc, char* argv[])
{
	int i = 0;
	func(++i, i++);
	return 0;
}
C函数参数入栈顺序为从右到左,刚开始i=0,右边参数先入栈,即v2对应i++的值,为0,此时i=1,然后左边参数入栈,即v1对应++i的值,为2.

所以程序输出:2 0


28.虚函数内联

虚函数不可以是内联的,内联的意思是“在编译期间将调用之处用被调函数来代替”,如果编译器还不知道哪个函数将被调用,便会拒绝生成内联调用。


29.虚函数和静态函数函数名和参数都一样是不允许的,实例调用的时候不知道调用哪个。


30.关于int *p = new int[10]()

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


31.

#include <iostream>

using namespace std;

void func(void **p){
	p = (void **)1024UL;
}

void func2(void **p){
	*p = (void *)1024UL;
}

int main()
{
	void *p = NULL;
	func(&p);
	printf("%p\n", p);
	func2(&p);
	printf("%p\n", p);
	return 0;
}
//输出:
//00000000
//00000400


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值