《C和C++程序员面试秘笈》第6章 C++面向对象

1. 描述面向对象技术的基本概念

类 对象
抽象 封装 继承 重载 多态
  1. 类的基本概念 略

3. C++与C语言相比的改进

在求解问题的方法上进行的最大改进:面向对象

4. class和struct的区别

C语言中
	struct用来定义结构体类型,只能有成员变量,并没有成员函数
C++中
	定义类类型时
		struct成员的默认访问权限是publicclass默认访问权限是private
		struct默认是public继承,class默认继承是private继承
	C++模板中
		class/typename 后跟类型参数

5. C++类对象

利用对象的无参构造函数,去构造对象时,不需要加小括号

6. C++类private成员的访问

私有成员 只能被对象内部调用,不能被外部调用,也不能通过对象调用

7. 构造函数、析构函数调用顺序

当调用构造函数时,
	首先按各对象成员在类定义中的顺序(和参数列表的顺序无关)依次调用它们的构造函数,对这些对象初始化,
	最后再调用本身的函数体。
	也就是说,先调用对象成员的构造函数,再调用本身的构造函数。
析构函数
	和构造函数调用顺序相反:先构造,后析构

8. 静态成员变量的使用

普通成员变量,类的每个对象都有一份
静态成员变量,只有一份,类的所有对象共享,被当做类的全局变量

9. 与全局变量相比,类的静态成员变量 有什么优势

静态数据成员变量
	1、没有进入程序的全局名字空间,因此不会与其它全局名字冲突
	2、访问权限设置为private时,可以隐藏信息

10. 哪些情况下只能使用初始化列表

	1const、reference类型的成员变量,
	只能被初始化,不能做赋值操作,因此只能用初始化列表
	2、派生类的构造函数需要调用基类的构造函数的时候

11. 静态数据成员

1、静态数据成员,类内定义 类外初始化
2、静态成员函数、静态成员变量:都不属于类的对象,因此它们都不含有this指针
   因此不能调用非静态成员

12. 静态数据成员

静态数据成员:
	类内定义 类外初始化
	不受private控制符的作用
静态成员:
	通过类的对象调用 
	通过类名调用

13. main()函数执行前还会执行什么代码?

	首先,对全局对象进行构造
	然后,进入main()函数中
	再,进行局部对象的构造

14. 空类默认会产生哪些成员函数

	默认构造函数
	复制构造函数
	析构函数
	operator=(赋值构造函数)	

15. 构造函数 析构函数 是否可以重载

构造函数:
	可以有多个-->重载,可以带参数
析构函数:
	只能有一个,且不能带参数

17. 构造函数的使用

构造函数内 调用了重载的其他版本的构造函数 只是在栈上生成了一个临时对象,对本对象没有影响
构造函数的相互调用,会导致栈溢出

18. explicit构造函数的作用

普通构造函数 能够被隐式调用、显示调用
	obj a=12;//隐式调用
	obj b(12);//显示调用
explicit构造函数 只能被显示调用 
	explicit修饰了某一个构造函数后
	只能 obj c(12);//显示调用那个构造函数

19. explicit构造函数的作用

形参是对象时,传值进去构造临时对象时,是隐式调用构造函数
explicit构造函数,在传参进去时,构造不出来对象,因为只能显示调用构造函数,禁止了隐式调用构造函数

20. 析构函数的作用是什么

析构函数
	是为了在对象不被使用之后释放它的资源
virtual 
	虚函数:(普通的虚函数)是为了实现多态
	虚析构函数:只有当一个类被用来作为基类的时候,才会把析构函数写成虚析构函数

21. 析构函数执行顺序

析构函数执行顺序 与 构造函数执行顺序相反

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//基类A
class A
{
public:
	A(int aa) :a(aa) {};
	~A() { cout << "Destructure A!" << a << endl; getchar(); };
private:
	int a;
};

//A的派生类B
class B : public A
{
public:
	B(int aa = 0, int bb = 0) :A(aa), b(bb) {};
	~B() { cout << "Destructure B!" << b << endl; };
private:
	int b;
};
int main()
{
	B obj1(5), obj2(6,7);
	system("pause");
	return 0;
}
//obj1 obj2这两个对象都是在栈上,所以先进后出,obj2先析构 obj1再析构
//
//Destructure B!7
//Destructure A!6
//
//Destructure B!0
//Destructure A!5

22. 复制构造函数

复制构造函数的调用
	1、一个对象以值传递的方式传入函数体
	2、一个对象以值传递的方式从函数返回
	3、一个对象需要通过另外一个对象初进行始化
深拷贝 浅拷贝
	成员变量是指针,并且指针指向了堆区内存
	浅拷贝时,老对象的指针和新对象的指针指向同一个堆区,两次释放导致程序崩溃
	深拷贝时,先为新对象的指针申请一块新的堆内存,然后把老对象堆区的数据拷贝进去
	
operator=()是赋值构造函数

23. 编译器何时才会生成默认的拷贝构造函数

用到了拷贝构造函数(copy constructor)
	没有自定义拷贝构造函数,会生成默认的...
没有用到拷贝构造函数
	没有自定义拷贝构造函数,不生成默认...

24. 写一个派生类的拷贝函数

派生类的构造函数
	对基类对象进行初始化时,使用初始化列表,调用基类的构造函数对基类对象进行初始化
	对本身初始化时,使用初始化列表或者赋值语句

25. 复制构造函数 赋值函数 的区别

1、
	复制构造函数:是使用一个已存在的对象来初始化 新对象的内存区域
	赋值函数:对一个已经被初始化的对象进行operator=操作
2、数据成员包含指针对象的时候,有两种处理需求:一种是复制指针对象,一种是引用指针对象
	复制构造函数:在大多数情况下是复制
	赋值函数:在大多数情况下是引用对象
3、实现不一样
	复制构造函数:
		首先它是一个构造函数,调用的时候是通过参数传递进来的那个对象来 初始化产生一个新对象
	赋值函数:
		把一个对象赋值给一个原有的对象
			如果原有的那个对象中有内存分配,先把内存释放掉,
			而且还要检查一下两个对象是不是同一个对象,如果是同一个对象,就不做任何操作

26. 编写string类的构造函数、析构函数、赋值函数

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class mystrng
{
public:
	mystrng(const char * str = nullptr);		//1、普通构造函数
	mystrng(const mystrng &other);				//2、复制构造函数
	~mystrng();									//3、析构函数
	mystrng & operator=(const mystrng &other);	//4、赋值函数
private:
	char *buffer;								//保存字符串
};

//1、普通构造函数
mystrng::mystrng(const char * str)
{
	cout << "constructor mystrng(const char * str = nullptr) " << endl;
	if (buffer != nullptr)
	{
		buffer = new char[strlen(str) + 1];
		strcpy(buffer, str);
	}
	else if(buffer == nullptr)
	{
		buffer = new char[1];
		*buffer = '\0';
	}
}

//2、复制构造函数
mystrng::mystrng(const mystrng &other)
{
	cout << "constructor mystrng(const mystrng &other) " << endl;
	buffer = new char[strlen(other.buffer) + 1];
	strcpy(buffer, other.buffer);
}

//3、析构函数
mystrng::~mystrng()
{
	cout << "deconstructor ~mystrng() " << endl;
	if (buffer != nullptr)
	{
		delete[] buffer;
		buffer = nullptr;
	}
}

//4、赋值函数
mystrng & mystrng::operator=(const mystrng &other)
{
	cout << "mystrng & operator=(const mystrng &other) " << endl;
	if (this == &other)
	{
		return *this;
	}
	delete[]buffer;
	buffer = new char[strlen(other.buffer) + 1];
	strcpy(buffer, other.buffer);
	return *this;
}
int main()
{
	mystrng a("hello");//1、普通构造函数
	mystrng b("world");//1、
	mystrng c(a);//2、复制构造函数
	c = b;//4、赋值函数

	system("pause");
	return 0;
}

27. 对象的引用

class A
A a;
A & b = a;//b为对象a的一个引用,不调用构造函数或者赋值函数
  1. 类的临时对象

29. 复制构造函数和析构函数

函数返回对象时
	内部的对象,在返回这个对象给外部时,
	会生成一个临时的对象,这个,临时的对象用来给外部的对象进行复赋值/初始化
  1. 静态成员和临时对象

31. 临时对象在什么情况下产生?

class A;

void fun1(A a)//值传递:生成临时对象
{
	a.print();
}

A fun2()
{
	A b;		//局部对象b
	return b;	//值返回:生成临时对象,临时对象给外部的对象赋值
}

1、函数内部的局部变量(出现在程序代码中,有自己的名字)
2、临时变量
	真正的临时对象是看不见的,它不会出现在程序代码中;
3、两种情况下产生临时对象
	1. 参数按值传递
	2. 返回值按值传递
4、大多数情况下,它影响程序的执行效率,所以尽量避免临时对象的产生
	1. 使用 按引用传递 代替 按值传递(必须是实在的、可引用的对象)

32. 函数重载

同名函数具有相同或者相似的功能,但是参数不同
函数名 
	经过C++编译器处理后包含了原函数名、函数参数类型、返回类型
    而C语言不会对函数名进行处理

33. 函数才重载的声明

注意:
	声明时,形参中的const修饰词会被忽略
	返回值类型,不能用来区分函数重载
	在一组重载函数中,只能有一个函数被指定为 extern "C"

34. 重载和覆盖有什么区别

函数重载 overload
	1.同一作用域
	2.函数名字相同,形参列表不同,返回类型可以不同
	3.减轻程序员起名字、记名字的负担
	是一种语法规则,由编译器在编译阶段完成,不属于面向对象的编程
覆盖
	是子类重写父类的虚函数
	1.函数名相同,形参列表相同,返回值类型相同
	由运行阶段决定,是面向对象编程的重要特征

35. MyString类的编写

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class MyString
{
public:
	MyString(char *s)//有参构造函数
	{
		str = new char[strlen(s) + 1];
		strcpy(str, s);
	}
	~MyString()//析构函数
	{
		if (str != nullptr)
		{
			delete[]str;
			str = nullptr;
		}
	}
	MyString & operator=(MyString &string)//operator=
	{
		if (this == &string)
			return;
		delete[]str;
		str = new char[strlen(string.str) + 1];
		strcpy(str, string.str);
		return *this;
	}
	MyString & operator+(MyString & string)//operator+ 不改变被加对象
	{
		MyString *pstring = new MyString((char *)"");
		pstring->str = new char[strlen(str) + strlen(string.str) + 1];
		strcpy(pstring->str, str);
		strcat(pstring->str, string.str);
		return *pstring;
	}
	void  print()
	{
		cout << str << endl;
	}
private:
	char *str;
};
int main()
{
	
	system("pause");
	return 0;
}

36. 各种运算符重载函数的编写

题目:
	用C++实现一个string类,它具有比较、连接、输入、输出功能。
解析:
	需要重载下面的运算符
		1< > == !=
		2+= 
		3<<  >>

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class String
{
public:
	String();					//1、默认构造函数
	String(int n, char c);		//2、普通构造函数
	String(const char * source);//3、普通构造函数
	String(const String &s);	//4、复制构造函数
	String& operator=(char *s);				//5、重载=,实现字符串赋值
	String& operator=(const String & s);	//6、重载=,实现对象赋值
	~String();								//7、析构函数
	char& operator[](int i);				//8、重载[],实现数组运算
	const char& operator[](int i)const;		//9、重载[],实现数组运算(对象为常量)
	String& operator+=(const char* s);		//10、重载+=,实现与字符串相加
	String& operator+=(const String& s);	//11、重载+=,实现与对象相加
	friend ostream& operator<<(ostream & out,String& s);//12、重载<<,实现输出流
	friend istream& operator>>(istream & in, String& s);//12、重载>>,实现输入流
	friend bool operator<(const String& left, const String& right);//13、重载<
	friend bool operator>(const String& left, const String& right);//14、重载>
	friend bool operator==(const String& left, const String& right);//15、重==
	friend bool operator!=(const String& left, const String& right);//16、重载!=
	char*& getData();//17、获取data指针
	int length();	//18、获取字符串长度
private:
	int size;//data表示的字符串的长度
	char* data;//指向字符串数据
	   	  
};



//注意:
//	在C++ 中 
//	new char[]() 编译器默认将其初始化为0,
//	new char[]   则不会初始化


String::String()					//1、默认构造函数
{
	data = new char[1]();
	size = 0;
}
String::String(int n, char c)		//2、普通构造函数
{
	data = new char[n + 1]();
	size = n;
	char* temp = data;
	while (n--)
		*temp++ = c;
	//*temp = '\0';
}
String::String(const char * source) //3、普通构造函数
{
	size = strlen(source);
	data = new char[size = strlen(source) + 1]();
	strcpy(data, source);
}
String::String(const String &s)		//4、复制构造函数
{
	//注意:
	//	在C++ 中 
	//	new char[]() 编译器默认将其初始化为0,
	//	new char[]   则不会初始化
	data = new char[s.size + 1]();
	strcpy(data, s.data);
	size = s.size;
}
String & String::operator=(char *s)				//5、重载=,实现字符串赋值
{
	if (data != nullptr)
		delete[]data;
	size = strlen(s);
	data = new char[ size + 1]();
	strcpy(data, s);
	return *this;
}
String & String::operator=(const String & s)	//6、重载=,实现对象赋值
{
	if (this == &s)
		return *this;
	if (data != nullptr)
		delete[]data;
	size = s.size;
	data = new char[size + 1]();
	strcpy(data, s.data);
	return *this;
}
String::~String()								//7、析构函数
{
	if (data != nullptr)
	{
		delete[]data;
		data = nullptr;
		size = 0;
	}
}
char & String::operator[](int i)				//8、重载[],实现数组运算
{
	return data[i];
}
const char& String::operator[](int i)const		//9、重载[],实现数组运算(对象为常量)
{
	return data[i];
}
String& String::operator+=(const char* s)		//10、重载+=,实现与字符串相加
{
	if (s == nullptr)
		return *this;
	size += strlen(s);
	char* temp = data;
	data = new char[size + 1]();
	strcpy(data, temp);
	delete[]temp;
	strcat(data, s);
	return *this;
}
String& String::operator+=(const String& s)		//11、重载+=,实现与对象相加
{
	size += s.size;
	char* temp = data;
	data = new char[size + 1]();
	strcpy(data, temp);
	delete[]temp;
	strcat(data, s.data);
	return *this;
}
ostream& operator<<(ostream & out, String& s)//12、重载<<,实现输出流
{
	out << s.data;
	return out;
}
istream& operator>>(istream & in, String& s)//12、重载>>,实现输入流
{
	char temp[1024] = { '\0' };
	in.getline(temp, 1024);
	s = temp;
	return in;
}
bool operator<(const String& left, const String& right)//13、重载<
{
	int i = 0;
	while (left[i] == right[i] && left[i] != '\0'&&right[i] != '\0')
		i++;
	return (left[i] - right[i] < 0 ? true : false);
}
bool operator>(const String& left, const String& right)//14、重载>
{
	int i = 0;
	while (left[i] == right[i] && left[i] != '\0'&&right[i] != '\0')
		i++;
	return (left[i] - right[i] > 0 ? true : false);
}
bool operator==(const String& left, const String& right)//15、重载==
{
	int i = 0;
	while (left[i] == right[i] && left[i] != '\0'&&right[i] != '\0')
		i++;
	return ((left[i] == right[i]) ? true : false);
}
bool operator!=(const String& left, const String& right)//16、重载!=
{
	int i = 0;
	while (left[i] == right[i] && left[i] != '\0'&&right[i] != '\0')
		i++;
	return ((left[i] != right[i]) ? true : false);
}
char*& String::getData()//17、获取data指针
{
	return data;
}
int String::length()	//18、获取字符串长度
{
	return size;
}

int main()
{
	String str(3, 'a');	//普通构造函数
	String str1(str);	//复制构造函数
	String str2("asdf");//普通构造函数
	String str3;		//默认构造函数

	cout << "str: " << str << endl;
	cout << "str1: " << str1 << endl;
	cout << "str2: " << str2 << endl;
	cout << "str3: " << str3 << endl;

	str3 = str2;	//operator=
	cout << "str3: " << str3 << endl;
	str3 = "12ab";	//operator=
	cout << "str3: " << str3 << endl;

	cout << "str3[2]= " << str3[2] << endl;

	str3 += "111";	//operator+=
	cout << "str3: " << str3 << endl;
	str3 += str1;	//operator+=
	cout << "str3: " << str3 << endl;

	cin >> str1;
	cout << "str1: " << str1 << endl;

	String t1 = "1234";
	String t2 = "1234";
	String t3 = "12345";
	String t4 = "12335";
	cout << "t1 == t2 ? " << (t1 == t2) << endl;
	cout << "t1 < t3 ? " << (t1 < t3) << endl;
	cout << "t1 > t4 ? " << (t1 > t4) << endl;
	cout << "t1 != t4 ? " << (t1 != t4) << endl;

	system("pause");
	return 0;
}
发布了15 篇原创文章 · 获赞 11 · 访问量 8277

猜你喜欢

转载自blog.csdn.net/liangwenhao1108/article/details/104666077