面向对象基础知识
1、面向对象3个要素:封装、继承、多态。面向对象中所有的对象都可以归属为一个基类。
类成员
2、成员变量的访问方式:
private:可以被该类的成员方法和友元函数访问,但不能被该类的对象访问。
protected:可以被该类的成员方法、友元函数及子类的方法访问,但不能被该类的对象访问。
public:可以被该类的成员方法、友元函数及子类的方法访问,也能被该类的对象访问。
多态
3、动态多态:继承+虚函数(常用)
静态多态:泛型,STL(模板技术)
template <typename vv>
void fun(const vv& v){
v.fun1(); //这样就可以在main中,根据传入的fun中参数的对象类型,去调用各自类中的fun1方法
}
函数多态:重载(相同的函数名,不同的参数列表,其中有一种特殊的,const成员函数和非const成员函数同样可以形成重载)
class Test
{
protected:
int x;
public:
Test (int i):x(i){ }
void fun() const{
cout << "fun() const called " << endl;
}
void fun(){
cout << "fun() called " << endl;
}
};
int main()
{
Test t1 (10);
const Test t2 (20);
t1.fun(); //fun() called
t2.fun(); //fun() const called
return 0;
}
宏多态:
#define ADD(A,B) (A)+(B)
int main()
{
int i = ADD(1,2); //两个整数相加
string s = ADD("hello","world"); //两个字符串相加
}
继承
4、派生类总是可以自动转换为基类的引用类型,基类转换为派生类需要在确定安全的情况下,使用强制转换来进行转换。
复制构造函数
5、深拷贝和浅拷贝
浅拷贝:只是对对象中的数据成员进行简单的复制(默认复制构造函数)
class Student
{
private:
int num;
char *name;
public:
Student();
~Student();
};
Student::Student()
{
name = new char(20);
cout << "Student" << endl;
}
Student::~Student()
{
cout << "~Student " << (int)name << endl;
delete name;
name = NULL;
}
int main()
{
Student s1;
Student s2(s1);// 复制对象
return 0;
}
执行结果:调用一次构造函数,调用两次析构函数,两个对象的指针成员所指内存相同,这会导致什么问题呢?name指针被分配一次内存,但是程序结束时该内存却被释放了两次,会导致崩溃!
深拷贝:重新分配空间(重载默认的拷贝构造函数)
Student::Student(const Student &s)
{
name = new char(20);
memcpy(name, s.name, strlen(s.name));
cout << "copy Student" << endl;
}
执行结果:调用一次构造函数,一次自定义拷贝构造函数,两次析构函数。两个对象的指针成员所指内存不同。
6、有3种情况会使用复制构造函数:
一个对象以值传递的方式传入函数体;
一个对象以值传递的方式从函数返回
一个对象需要通过另外一个对象进行初始化。
函数的定义
7、求最大公约数:通过不断的求余数,并更新第一个数的值为当前第二个数的值,直到余数为0,返回第一个数。(辗转相除法)
int gcd(int v1, int v2) {
while (v2) {
int temp = v2;
v2 = v1 % v2;
v1 = temp;
}
return v1;
}
8、在类内部声明或定义的成员函数叫做内联(inline)函数:函数的代码被放入符号表中,在使用时直接进行替换,适用于代码量较小的函数。
class temp{
public:
int amount;
temp(int amount){
this->amount = amount;
}
void test(){ //默认的都是内联函数,可以不加inline
cout << "default inline" << endl;
}
//普通成员函数,在类内声明时前面可以不加inline
void print_amount();
}
//在类外定义函数体,必须在前面加上inline关键字
inline void temp:: print_amount(){
cout << amount << endl;
}
类成员函数
9、外部类可以使用宏定义等特殊的方法来实现访问类的私有成员,但是是一种非正规的方法,会破坏类的封装性。
#define private public
#define protected public
模板
10、函数模板技术使得程序能够使用不同的参数类型调用相同的函数,相比于重载写的代码量还要少。(是一种多态的体现)
template <typename T>
T Add(T a, T b)
{
T result;
result = a + b;
return result;
}
int main()
{
cout << Add(1, 2) << endl; //3
cout << Add(string("1"), string("2")) << endl; //12
return 0;
}
11、类模板:常用于建立包含其他类型的容器,例如动态数组、队列、链表和堆栈等。
template<class T>
class A{
public:
T a;
T b;
T hy(T c, T &d);
};
容器
12、顺序容器:数组和链表的推广,以严格的线性形式组织在一起。
vector<T>、deque<T>、list<T>
vector可以通过at()或[]来访问其中的元素。
关联容器:提供一个Key实现对元素的高效查找和读取,适用于插入和删除操作。
map(Key,Value)、set、multimap(Key,Value)、multiset
vector、map、set的区别:
a. map和set使用了非常高效的平衡二叉树:红黑树(查找、插入、删除等操作的时间复杂度为O(logn) )。
b. set和vector的区别在于set不能包含重复的数据,而vector可以。
c. set和map的区别在于set只含有Key,而Map有一个Key和Key所对应的Value两个元素。
13、迭代器的范围:begin(指向第一个元素)和end(指向最后一个元素的下一个位置,并不是容器的元素)之间的范围。
泛型编程
14、以独立于任何特定类实现的方式编写代码,针对不同的类型提供通用的实现。(基础就是模板技术)