1. 描述面向对象技术的基本概念
类 对象
抽象 封装 继承 重载 多态
- 类的基本概念 略
3. C++与C语言相比的改进
在求解问题的方法上进行的最大改进:面向对象
4. class和struct的区别
C语言中
struct用来定义结构体类型,只能有成员变量,并没有成员函数
C++中
定义类类型时
struct成员的默认访问权限是public,class默认访问权限是private
struct默认是public继承,class默认继承是private继承
C++模板中
class/typename 后跟类型参数
5. C++类对象
利用对象的无参构造函数,去构造对象时,不需要加小括号
6. C++类private成员的访问
私有成员 只能被对象内部调用,不能被外部调用,也不能通过对象调用
7. 构造函数、析构函数调用顺序
当调用构造函数时,
首先按各对象成员在类定义中的顺序(和参数列表的顺序无关)依次调用它们的构造函数,对这些对象初始化,
最后再调用本身的函数体。
也就是说,先调用对象成员的构造函数,再调用本身的构造函数。
析构函数
和构造函数调用顺序相反:先构造,后析构
8. 静态成员变量的使用
普通成员变量,类的每个对象都有一份
静态成员变量,只有一份,类的所有对象共享,被当做类的全局变量
9. 与全局变量相比,类的静态成员变量 有什么优势
静态数据成员变量
1、没有进入程序的全局名字空间,因此不会与其它全局名字冲突
2、访问权限设置为private时,可以隐藏信息
10. 哪些情况下只能使用初始化列表
1、const、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;
class A
{
public:
A(int aa) :a(aa) {};
~A() { cout << "Destructure A!" << a << endl; getchar(); };
private:
int a;
};
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;
}
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);
mystrng(const mystrng &other);
~mystrng();
mystrng & operator=(const mystrng &other);
private:
char *buffer;
};
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';
}
}
mystrng::mystrng(const mystrng &other)
{
cout << "constructor mystrng(const mystrng &other) " << endl;
buffer = new char[strlen(other.buffer) + 1];
strcpy(buffer, other.buffer);
}
mystrng::~mystrng()
{
cout << "deconstructor ~mystrng() " << endl;
if (buffer != nullptr)
{
delete[] buffer;
buffer = nullptr;
}
}
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");
mystrng b("world");
mystrng c(a);
c = b;
system("pause");
return 0;
}
27. 对象的引用
class A
A a;
A & b = a;
- 类的临时对象
29. 复制构造函数和析构函数
函数返回对象时
内部的对象,在返回这个对象给外部时,
会生成一个临时的对象,这个,临时的对象用来给外部的对象进行复赋值/初始化
- 静态成员和临时对象
31. 临时对象在什么情况下产生?
class A;
void fun1(A a)
{
a.print();
}
A fun2()
{
A 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)
{
if (this == &string)
return;
delete[]str;
str = new char[strlen(string.str) + 1];
strcpy(str, string.str);
return *this;
}
MyString & operator+(MyString & string)
{
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();
String(int n, char c);
String(const char * source);
String(const String &s);
String& operator=(char *s);
String& operator=(const String & s);
~String();
char& operator[](int i);
const char& operator[](int i)const;
String& operator+=(const char* s);
String& operator+=(const String& s);
friend ostream& operator<<(ostream & out,String& s);
friend istream& operator>>(istream & in, String& s);
friend bool operator<(const String& left, const String& right);
friend bool operator>(const String& left, const String& right);
friend bool operator==(const String& left, const String& right);
friend bool operator!=(const String& left, const String& right);
char*& getData();
int length();
private:
int size;
char* data;
};
String::String()
{
data = new char[1]();
size = 0;
}
String::String(int n, char c)
{
data = new char[n + 1]();
size = n;
char* temp = data;
while (n--)
*temp++ = c;
}
String::String(const char * source)
{
size = strlen(source);
data = new char[size = strlen(source) + 1]();
strcpy(data, source);
}
String::String(const String &s)
{
data = new char[s.size + 1]();
strcpy(data, s.data);
size = s.size;
}
String & String::operator=(char *s)
{
if (data != nullptr)
delete[]data;
size = strlen(s);
data = new char[ size + 1]();
strcpy(data, s);
return *this;
}
String & String::operator=(const String & s)
{
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()
{
if (data != nullptr)
{
delete[]data;
data = nullptr;
size = 0;
}
}
char & String::operator[](int i)
{
return data[i];
}
const char& String::operator[](int i)const
{
return data[i];
}
String& String::operator+=(const char* s)
{
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)
{
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)
{
out << s.data;
return out;
}
istream& operator>>(istream & in, String& s)
{
char temp[1024] = { '\0' };
in.getline(temp, 1024);
s = temp;
return in;
}
bool operator<(const String& left, const String& right)
{
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)
{
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)
{
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)
{
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()
{
return data;
}
int String::length()
{
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;
cout << "str3: " << str3 << endl;
str3 = "12ab";
cout << "str3: " << str3 << endl;
cout << "str3[2]= " << str3[2] << endl;
str3 += "111";
cout << "str3: " << str3 << endl;
str3 += str1;
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;
}