匠心C++学习目录
数据类型与break和continue
STL容器
常见排序算法
程序内存模型
函数相关操作
类和对象相关操作
模板c++
谓词和仿函数
文章目录
一、构造函数
类构造函数根据构造方式不同,分为无参构造,有参构造和拷贝构造。其中拷贝构造又可分为深拷贝和浅拷贝。
1、无参构造
class Person
{
public:
Person()
{}
}
无参构造,可由机器自动生成
2、有参构造
class Person
{
public:
Person(int age)
{
this->age = age;
}
int age;
};
3、拷贝构造
class Person
{
public:
Person(Person P1)
{
this->age = p1.age;
}
int age;
};
注意:
1)拷贝构造也可通过机器自动生成,即无需自定义
2)拷贝构造分为深拷贝和浅拷贝。这种分类一般在类中包含指针时,需要加以区分,比如
class Person
{
public:
Person(Person P1)
{
this->age = p1.age;
this->height = new int(*(p1.height));
}
~Person()
{
if(height != nullptr)
{
delete height;
}
height = nullptr;
}
int age;
int* height;
};
上面的代码就是深拷贝,即对于指针height,其指向不再是P1中height的指向内存空间,而是另开辟了新的内存空间。
因此浅拷贝就是指针指向仍然是P1中指针指向的内存空间。浅拷贝一般出现在机器自动生成的默认构造函数中
二、成员变量或函数的访问方式
class Person
{
public:
Person(Person P1)
{
this->age = p1.age;
this->height = new int(*(p1.height));
}
~Person()
{
if(height != nullptr)
{
delete height;
}
height = nullptr;
}
int age;
int* height;
};
int main()
{
Person P1;
P1.age = 40;
return 0;
}
但是如果成员函数或变量是静态成员函数或变量,就多了一种调用方式,即直接通过类名调用(这说明静态函数或变量是共享的,即Person p1, p2的静态函数是共享的)
class Person
{
public:
Person(Person P1)
{
this->age = p1.age;
this->height = new int(*(p1.height));
}
~Person()
{
if(height != nullptr)
{
delete height;
}
height = nullptr;
}
static int age;
int* height;
};
int main()
{
Person P1;
Person::age = 40;
return 0;
}
静态的成员函数只能访问静态的成员变量,即
class Person
{
public:
Person(Person P1)
{
this->age = p1.age;
this->height = new int(*(p1.height));
}
~Person()
{
if(height != nullptr)
{
delete height;
}
height = nullptr;
}
static void fun()
{
age = 90;
cout << "age == " << age << endl;
weight = 100 //这是有问题的,因为静态函数不能调用非静态的成员函数
}
static int age;
int weight;
int* height;
};
Persion::age = 0
int main()
{
Person P1;
Person::age = 40;
return 0;
}
注意:静态成员变量需要在类内声明,类外初始化
三、const修饰成员函数
四、友元
友元的作用是让一个类或者函数可以访问另一个类的私有成员变量或者函数
1、友元的实现方式
类做友元
全局函数做友元
成员函数做友元
2、成员函数做友元
需要在类中加入
friend T global(class m);
for an example:
class Person
{
friend void print_val(Person* p1);
public:
Person()
{
this->m_height = "180";
this->m_weight = "65";
}
public:
string m_height;
private:
string m_weight;
};
void print_val(Person* p1)
{
cout << p1->m_weight << endl;
cout << p1->m_height << endl;
}
注意的是:
1)如果没有friend,print_val不能访问m_weight成员变量,因为其为Person的私有变量;但是可以访问m_height;当加上friend,即可以访问
2、类做友元
for an example:
class Person;
class Company
{
Company()
{
person = new Person();
}
Person* person;
};
class Person
{
friend class Company;
public:
Person()
{
this->m_height = "180";
this->m_weight = "65";
}
public:
string m_height;
private:
string m_weight;
};
void main()
{
Company* company = new Company();
cout << company->person->m_weight << endl;
cout << company->person->m_height << endl;
}
3、成员函数做友元
class Person;
class Company
{
Company()
{
person = new Person();
}
void visit()
{
cout << person->m_weight << endl;
cout << person->m_height << endl;
}
Person* person;
};
class Person
{
friend void Company::visit();
public:
Person()
{
this->m_height = "180";
this->m_weight = "65";
}
public:
string m_height;
private:
string m_weight;
};
void main()
{
Company* company = new Company();
company->visited;
}
五、运算符重载
运算符重载分为成员函数重载和全局函数重载。且运算符重载支持函数重载(即一个运算符名,多个函数)
1、加号重载
1)成员函数重载+号
class Person
{
public:
Person()
{
this->m_height = 180;
this->m_weight = 65;
}
Person operator+(Person& p1)
{
Person temp;
temp.m_height = this->m_height + p1.m_height;
temp.m_weight = this->m_weight + p1.m_weight;
}
public:
int m_height;
int m_weight;
};
void main()
{
Person* p1 = new Person();
Person* p2 = new Person();
Person p3 = *p1 + *p2;
cout << p3.m_height << ", " << p3.m_weight << endl;
}
成员函数运算符重载的本质是p3 = p1.operator+(p2)
2)全局函数重载+号
class Person
{
public:
Person()
{
this->m_height = 180;
this->m_weight = 65;
}
public:
int m_height;
int m_weight;
};
Person operator+(Person& p1, Person& p2)
{
Person temp;
temp.m_height = p1.m_height + p2.m_height;
temp.m_weight = p1.m_weight + p2.m_weight;
}
void main()
{
Person* p1 = new Person();
Person* p2 = new Person();
Person p3 = *p1 + *p2;
cout << p3.m_height << ", " << p3.m_weight << endl;
}
全局函数运算符重载的本质是p3 = operator+(p1,p2)
2、左移运算符重载
只能全局运算符重载
class Person
{
public:
Person()
{
this->m_height = 180;
this->m_weight = 65;
}
public:
int m_height;
int m_weight;
};
ostream& operator<<(Person& p1, ostream& out)
{
out << p1.m_height << "," << p1.m_weight << endl;
}
void main()
{
Person* p1 = new Person();
cout << p1 << "game over" << endl;
}
3、递增运算符重载
1)前置递增运算符重载
class Person
{
public:
Person()
{
this->m_height = 180;
this->m_weight = 65;
}
Person& operator++()
{
m_height++;
m_weight++;
return *this;
}
public:
int m_height;
int m_weight;
};
void main()
{
Person* p1 = new Person();
++*p1;
cout << p1->m_height << ", " << p1->m_weight << endl;
}
2)后置递增运算符重载
class Person
{
public:
Person()
{
this->m_height = 180;
this->m_weight = 65;
}
Person operator++(int)
{
Person temp = *this
m_height++;
m_weight++;
return temp;
}
public:
int m_height;
int m_weight;
};
void main()
{
Person* p1 = new Person();
cout << (*p1++).m_height << ", " << (*p1++).m_weight << endl;
cout << (*p1).m_height << ", " << (*p1).m_weight << endl;
}
可以看出,前置递增是返回引用,但是后置递增是值返回
4、赋值运算符重载
需要使用深拷贝代替浅拷贝,因为可能会发生野指针问题
class Person
{
public:
Person(int age)
{
//将年龄数据开辟到堆区
m_Age = new int(age);
}
//重载赋值运算符
Person& operator=(Person &p)
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
//编译器提供的代码是浅拷贝
//m_Age = p.m_Age;
//提供深拷贝 解决浅拷贝的问题
m_Age = new int(*p.m_Age);
//返回自身
return *this;
}
~Person()
{
if (m_Age != NULL)
{
delete m_Age;
m_Age = NULL;
}
}
//年龄的指针
int *m_Age;
};
void test01()
{
Person p1(18);
Person p2(20);
Person p3(30);
p3 = p2 = p1; //赋值操作
cout << "p1的年龄为:" << *p1.m_Age << endl;
cout << "p2的年龄为:" << *p2.m_Age << endl;
cout << "p3的年龄为:" << *p3.m_Age << endl;
}
int main() {
test01();
//int a = 10;
//int b = 20;
//int c = 30;
//c = b = a;
//cout << "a = " << a << endl;
//cout << "b = " << b << endl;
//cout << "c = " << c << endl;
system("pause");
return 0;
}
六、继承
1、继承的方式有public,private, protected三种。区别在于父类(或者叫基类)的变量或函数在子类中是那种形式(public,private, protected)。
class Base
{};
class Son:public Base
{};
2、子类中可以有与父类同名的成员函数(同名即可,不需要形参一致),且子类可以调用父类的那个同名函数,但是需要加作用域。如果没有加作用域,默认调用子类中的那个函数(可以理解成覆盖了父类函数)
class Base
{
public:
Base()
{
m_a = 10;
}
void visit()
{
cout<< "base::visit" << endl;
}
void visit( int )
{
cout<< "base::visit int " << endl;
}
int m_a;
};
class Son:public Base
{
public:
Son()
{
m_a = 10;
}
void visit()
{
cout<< "son::visit" << endl;
}
int m_a;
};
int main()
{
Base b1;
Son s1;
s1.visit(); //调用的是子类中的
s1.Base::visit();//调用的是父类的
s1.visited(10); //出错,因为没加作用域,认为访问子类同名函数,但是子类中那个函数没有占位参数,故出错
s1.Base::visit(10);//正确
return 0;
}
5、c++支持多父类继承,语法如下
class Base1
{};
class Base2
{};
class Base3
{};
class Base4
{};
class Son:public Base1, private Base2, protected Base1, public Base1,
{};
6、菱形继承
当父类中有相同的成员函数或者变量时,子类继承时,就增加了n倍内存空间,造成不必要浪费
比如
class Base1
{
public:
Base1()
{
m_a = 10;
}
int m_a;
};
class Base2 : public Base1
{
};
class Base3 : public Base1
{
};
class Son:public Base3, publicBase2
{
};
比如此时,son中就保存了2份m_a。
为了避免这个问题,可以使用虚继承来避免,即
class Base1
{
public:
Base1()
{
m_a = 10;
}
int m_a;
};
class Base2 : virtual public Base1
{
};
class Base3 : virtual public Base1
{
};
class Son:public Base3, publicBase2
{
};
七、多态
1、定义和模式
for an example:
class Base
{
public:
Base()
{
m_a = 10;
}
virtual void visit()
{
cout<< "base::visit" << endl;
}
int m_a;
};
class Son:public Base
{
public:
Son()
{
m_a = 10;
}
void visit()
{
cout<< "son::visit" << endl;
}
int m_a;
};
void test(Base& b1)
{
b1.visit();
}
int main()
{
Son s1;
test(s1);
return 0;
}
注意:
1)此时输出的是son::visit,即使用基类引用调用子类对象
2)一个virtual函数占用4个字节,即是虚函数表指针
3)正常的成员函数是不占用类的字节
2、纯虚函数和抽象类
存在一个纯虚函数的基类为抽象类
纯虚函数的形式
class Base
{
public:
Base()
{
m_a = 10;
}
virtual void visit() = 0;
int m_a;
};
class Son:public Base
{
public:
Son()
{
m_a = 10;
}
void visit()
{
cout<< "son::visit" << endl;
}
int m_a;
};
void test(Base& b1)
{
b1.visit();
}
int main()
{
Son s1;
test(s1);
return 0;
}
注意:
1)抽象类不能实例化,即
Base b1; //不行的
new Base; //不行的
2)子类需要重写抽象类的纯虚函数
3)基类指针调用子类函数(和虚函数同名那些)
class Base
{
public:
Base()
{
m_a = 10;
}
virtual void visit() = 0;
int m_a;
};
class Son:public Base
{
public:
Son()
{
m_a = 10;
}
void visit()
{
cout<< "son::visit" << endl;
}
int m_a;
};
int main()
{
Base* b1 = new Son;
b1->visit();
return 0;
}
3、虚析构
注意:
1)基类的构造函数不能为虚函数,但是析构函数可以为虚析构
2)虚析构作用是使用父类指针释放子类对象,即可以调用子类函数的析构函数
class Base
{
public:
Base()
{
m_a = 10;
}
virtual void visit() = 0;
virtual ~Base()
{
}
int m_a;
};
class Son:public Base
{
public:
Son()
{
*m_a = 10;
}
void visit()
{
cout<< "son::visit" << endl;
}
~Son()
{
if(m_a != nullptr)
{
delete m_a;
m_a = nullptr;
}
}
int* m_a = new int;
};
int main()
{
Base* b1 = new Son;
b1->visit();
return 0;
}
注意:
1)虚析构函数可以是纯虚析构函数,但是纯虚析构函数需要有声明和定义,即:
class Base
{
public:
Base()
{
m_a = 10;
}
virtual void visit() = 0;
virtual ~Base() = 0;
int m_a;
};
Base::~Base()
{
cout << "~Base()" << endl;
}
class Son:public Base
{
public:
Son()
{
*m_a = 10;
}
void visit()
{
cout<< "son::visit" << endl;
}
~Son()
{
if(m_a != nullptr)
{
delete m_a;
m_a = nullptr;
}
}
int* m_a = new int;
};
int main()
{
Base* b1 = new Son;
b1->visit();
return 0;
}