C++学习笔记三(类和对象)

一、示例

二、封装

1)访问权限:

2)struct和class的区别

3)成员属性私有化

三、对象特性

1)构造函数和析构函数

2)构造函数的分类和调用

3)构造函数的调用规则

4)深拷贝与浅拷贝

5)初始化列表

6)静态成员

7)this指针的用途

8)空指针访问成员函数

9)const修饰成员函数(常函数)

10)友元

四、运算符的重载

  1)重载加号+

  2)重载左移<<

  3)重载++运算符

  4)重载赋值运算符=

  5)重载关系运算符==

  6)重载函数调用运算符()----仿函数

五、继承

  1)继承语法

  2)继承方式

  3)构造函数和析构函数的执行顺序

  4)继承同名成员处理方式

  5)继承同名静态成员处理方式

  6)多继承

  7)菱形继承与虚继承

六、多态

  1)语法

  2)动态多态代码实例

  3)纯虚函数和抽象类

  4)虚析构和纯虚析构

一、示例

class Circle {
public://访问权限
    double r;
    double GetArea()
    {
        return r * r * 3.14;
    }
};
int main()
{
    Circle c1;
    c1.r = 10;
    cout << c1.GetArea() << endl;
    system("pause");
    return 0;
}

二、封装

1)访问权限:

①public公共权限        成员类内可以访问,类外也可以访问。

②protected保护权限  成员类内可以访问,类外不可以访问,但是儿子可以访问。

③private私有权限      成员类内可以访问,类外不可以访问,儿子也不可以访问。

2)struct和class的区别

struct成员默认权限是公有public,class成员默认权限是私有private。

3)成员属性私有化

class Student {
private:
    int age;
    string name;
    string num;

public:
    int GetAge()
    {
        return this->age;
    }
    void SetAge(int age)
    {
        this->age = age;
    }
};
int main()
{
    Student stu1;
    stu1.SetAge(21);
    cout << stu1.GetAge();

    system("pause");
    return 0;
}

三、对象特性

1)构造函数和析构函数

①构造函数语法:类名(){};

②析构函数语法:~类名(){};

2)构造函数的分类和调用

   分类:

①有参构造和无参构造

②拷贝构造函数:

Student(const Student& stu)
    {
        //.......
    }

   调用:

①调用默认参数时不需要加(),不然,Student stu()会被认为是一个函数声明:

Student stu;

②调用有参构造:

Student stu1=Student(10);

③调用拷贝构造:

Student stu2=Student(stu1);

④匿名对象:

Student(10);//当前行执行结束,系统会立即回收掉匿名对象

⑤隐式转换法:

Student stu1=10;//相当于Student stu1=Student(10)
Student stu2=stu1;//相当于拷贝构造

3)构造函数的调用规则:

①创建一个类,C++编译器会给每个类添加至少三个函数:默认构造,析构函数,拷贝构造。

②如果用户定义有参构造函数,那么编译器将不再提供无参构造函数,但是会提供默认拷贝构造函数。

③如果用户定义拷贝构造函数,那么C++将不会提供其他构造函数。

4)深拷贝与浅拷贝

浅拷贝:

Student(const Student &stu)
{
    age=stu.age;
}

深拷贝(重新再堆区创建一块内存保存数据):

Student(const Student &stu)
{
    age=new int(*stu.age);
}

总结:如果有属性是在堆区开辟的内存,就要自己构造拷贝函数,防止浅拷贝带来的问题。

 5)初始化列表

Student(int a, string b, string c) :age(a),name(b),num(c)
    {

    }

等价于:

Student(int age,string name,string num)
    {
        this->age = age;
        this->name = name;
        this->num = num;
    }

6)静态成员

①定义:在成员前面加上static称为静态成员。

②调用静态成员(两种方法):

通过对象访问:

Person p;
p.func();

通过类名访问:

Person::func();

③静态成员函数可以访问静态成员变量,但是不可以访问非静态成员变量,因为无法区分到底是哪个对象的变量。

 7)this指针的用途

①当形参和成员变量同名时,可以用this来区分;

②在类的非静态成员函数中返回对象本身,可以使用return *this。

8)空指针访问成员函数

class Student {
private:
    int age;
    string name;
    string num;
public:
    Student(int age,string name,string num)
    {
        this->age = age;
        this->name = name;
        this->num = num;
    }
    void ShowAge()
    {
        if (this == NULL)
            return;
        cout << age << endl;
    }
    void ShowName()
    {
        cout << "hello world" << endl;
    }

};
int main()
{
    Student* s = NULL;
    s->ShowAge();
    system("pause");
    return 0;
}

9)const修饰成员函数(常函数)

①常函数内不可以修改成员属性;

    成员属性声明时加关键字mutable后,就可以修改成员属性了。

class Student {
private:
    int age;
    mutable string name;
    string num;
public:
    Student(int age,string name,string num)
    {
        this->age = age;
        this->name = name;
        this->num = num;
    }
    void ShowName() const
    {
        this->name = "jack";
        cout << "hello world" << endl;
    }

};

②常对象只能调用常函数

class Student {
private:
    int age;
    mutable string name;
    string num;
public:
    Student(int age,string name,string num)
    {
        this->age = age;
        this->name = name;
        this->num = num;
    }
    void ShowAge()
    {
        if (this == NULL)
            return;
        cout << age << endl;
    }
    void ShowName() const
    {
        this->name = "jack";
        cout << "hello world" << endl;
    }

};
int main()
{
    const Student s;
    s.ShowName();
    system("pause");
    return 0;
}

10)友元

①友元的目的是为了让一个函数或者类访问另一个类中的私有成员。

    友元关键字为friend。

 ②友元的三种实现:

全局函数做友元

class Student {
    //全局函数可以访问当前类对象的私有成员
    friend void ShowInfor(Student s);
private:
    int age;
    mutable string name;
    string num;
public:
    Student(int age,string name,string num)
    {
        this->age = age;
        this->name = name;
        this->num = num;
    }
};

//全局函数
void ShowInfor(Student s)
{
    cout << s.age << endl;
}

类做友元

class Student {
    //另一个类可以访问当前类对象的私有成员
    friend class Teacher;

private:
    int age;
    mutable string name;
    string num;
public:
    Student(int age,string name,string num)
    {
        this->age = age;
        this->name = name;
        this->num = num;
    }
};
class Teacher {
private:
    string name;
    int age;
public:
    void Visit(Student s)
    {
        cout << s.name << s.age << endl;
    }
};

成员函数做友元

class Student {
    //Visit1可以访问当前类对象的私有成员,Visit2不能
    friend void Teacher::Visit1(Student s);

private:
    int age;
    mutable string name;
    string num;
public:
    Student(int age,string name,string num)
    {
        this->age = age;
        this->name = name;
        this->num = num;
    }
};
class Teacher {
private:
    string name;
    int age;
public:
    void Visit1(Student s)
    {
        cout << s.name << s.age << endl;
    }

    void Visit2(Student s)
    {

    }
};

四、运算符重载

1)加号运算符重载

    ①通过成员函数重载加号

    

class Person {
public:
    int age;
    Person(int age)
    {
        this->age = age;
    }
    //通过成员函数
    Person operator+(Person &p)
    {
        Person p1 = Person(30);
        p1.age = this->age + p.age;
        return p1;
    }
};

   ②通过全局函数重载加号

class Person {
public:
    int age;
    Person(int age)
    {
        this->age = age;
    }
    
};

//通过全局函数
Person operator+(Person& p1, Person& p2)
{
    Person p = Person(10);
    p.age = p1.age + p2.age;
    return p;
}
int main()
{
    Person p1 = Person(10);
    Person p2 = Person(20);
    Person p3 = p1 + p2;
    cout << p3.age << endl;

    system("pause");
    return 0;
}

 2)左移运算符

只能通过全局函数重载左移运算符

class Person {
public:
    string name;
    int age;
    Person(string a,int b):name(a),age(b)
    {

    }
};

ostream &operator<<(ostream &cout, Person &p)
{
    cout << p.name << endl << p.age;
    return cout;
}


int main()
{
    Person p = Person("jack", 19);
    cout << p << endl;
    system("pause");
    return 0;
}

 3)重载++运算符

class Person {
    friend ostream &operator<<(ostream& cout, Person& p);
private:
    string name;
    int age;
public:
    Person(string a,int b):name(a),age(b)
    {

    }
    //前置++运算符
    Person& operator++()
    {
        this->age++;
        return *this;
    }

    //后置++运算符,int用于区分前置运算符
    Person operator++(int)
    {
        Person temp = *this;
        age++;
        return temp;
    }
};

ostream &operator<<(ostream &cout, Person &p)
{
    cout << p.name << endl << p.age;
    return cout;
}


int main()
{
    Person p = Person("jack", 19);
    cout << ++p << endl;
    system("pause");
    return 0;
}

4)赋值运算符

class Person {
    friend ostream &operator<<(ostream& cout, Person& p);
private:
public:
    int* age;
    Person(int b)
    {
        age = new int(b);
    }
    ~Person()
    {
        if (age != NULL)
        {
            delete age;
            age = NULL;
        }
    }
    Person& operator==(Person p)
    {
        //先判断是否有属性在堆区,若有先释放干净,然后再深拷贝
        if (age != NULL)
        {
            delete age;
            age = NULL;
        }
        //深拷贝
        age = new int(*p.age);

        return *this;
    }
};

ostream &operator<<(ostream &cout, Person &p)
{
    cout << p.age<<endl;
    return cout;
}


int main()
{
    Person p1 = Person(19);
    Person p2 = Person(20);
    p2 = p1;
    cout << *p2.age << endl;
    system("pause");
    return 0;
}

5)关系运算符

class Person {
public:
    int age;
    Person(int b):age(b)
    {
        
    }
    bool operator==(Person p)
    {
        if (p.age == age)

            return true;

        else

            return false;
    }
};


int main()
{
    Person p1 = Person(19);
    Person p2 = Person(20);
    bool f = p1 == p2;
    cout << f;
    system("pause");
    return 0;
}

6)重载函数调用运算符(),也称为仿函数

class Person {
public:
    int age;
    Person(int b):age(b)
    {
        
    }
    void operator()(string s)
    {
        cout << s << endl;
    }
};
int main()
{
    Person p1 = Person(19);
    Person p2 = Person(20);
    p1("shidhapi");
    system("pause");
    return 0;
}

 五、继承

1)  继承语法:class   子类:  继承方式   父类

2)继承方式(3种):①公共继承;②保护继承;③私有继承。

3)构造函数和析构函数的执行顺序

父类构造函数->子类构造函数->子类析构函数->父类析构函数

4)继承同名成员处理方式

①同名成员属性处理方式

#include <iostream>
using namespace std;

class Base {
    public:
        Base() {
            m_A = 100;
        }
        int m_A;
};

class Son : public Base {
    public:
        Son() {
            m_A = 200;
        }
        int m_A;
};

void test01() {
    Son s;
    cout << "Son 下 m_A = " << s.m_A << endl;
    //如果通过子类对象 访问到父类中的同名成员 需要加作用域
    cout << "Base 下 m_A = " << s.Base::m_A << endl;
}
int main() {
    test01();
    return 0;
}

②同名成员函数处理方式

#include <iostream>
using namespace std;

class Base {
    public:
        Base() {
            m_A = 100;
        }

        void func() {
            cout << "Base fun()调用" << endl;
        }
        void func(int a) {
            cout << "Base fun(int a)调用" << endl;
        }
        int m_A;
};

class Son : public Base {
    public:
        Son() {
            m_A = 200;
        }
        void func() {
            cout << "Son fun()调用" << endl;

        }
        int m_A;
};

void test01() {
    Son s;
    s.func();//直接调用 调用是子类中的同名成员
    //如何调用父类中同名成员函数?
    s.Base::func();
    s.Base::func(100);
}
int main() {
    test01();
    return 0;
}

5)继承同名静态成员处理方式

①同名静态成员属性

#include <iostream>
using namespace std;
//继承中的同名静态成员处理方式

class Base {
    public:
        static int m_A;
};
int Base::m_A = 100;

class Son: public Base {
    public:

        static int m_A;
};
int Son::m_A = 200;

//同名静态成员属性
void test01() {
    Son s;
    //1.通过对象访问
    cout << "Son 下 m_A = " << s.m_A << endl;
    cout << "Base 下 m_A = " << s.Base::m_A << endl;
    //2.通过类名访问
    cout << "Son 下 m_A = " << Son::m_A << endl;
    cout << "Base 下 m_A = " << Base::m_A << endl;
    //第一个::代表通过类名方式访问,第二个::代表访问父类作用域下
    cout << "通过子类访问父类m_A = " << Son::Base::m_A << endl;
}

int main() {
    test01();
    return 0;
}

②同名静态成员函数

#include <iostream>
using namespace std;
//继承中的同名静态成员处理方式
class Base {
    public:
        static int m_A;

        static void func() {
            cout << "Base static void func()" << endl;
        }
        static void func(int a) {
            cout << "Base static void func(int a)" << endl;

        }
};
int Base::m_A = 100;

class Son: public Base {
    public:
        static void func() {
            cout << "Son static void func()" << endl;
        }
        static int m_A;
};
int Son::m_A = 200;

//同名静态成员属性
void test01() {
    Son s;
    //1.通过对象访问
    s.func();
    s.Base::func();
    //2.通过类外访问
    Son::func();
    Base::func();
    Son::Base::func();
    Son::Base::func(100);
}

int main() {
    test01();

    return 0;
}

6)多继承语法

class   子类:  继承方式   父类1,继承方式   父类2,。。。

多继承可能会引发父类中有同名成员出现,需要加作用域区分。实际开发中不建议用多继承

7)菱形继承与虚继承

①概念:2个派生类继承自同一个基类,又有同一个类继承这2个派生类,这种继承被称为菱形继承,或钻石继承。

②菱形继承的问题:这种继承方式也存在数据的二义性,这里的二义性是由于他们间接都有相同的基类导致的。 这种菱形继承除了带来二义性之外,还会浪费内存空间。

③解决方法:虚继承

class A
{
public :
    int num;
    A()
    {
        num = 10;
    }
};

class B :virtual public A
{
public:
    B()
    {
        num = 20;
    }
};

class C :virtual public A
{
public:
    C()
    {
        num = 30;
    }
};

class D :public B, public C
{
public:
    D()
    {
        num = 50;
    }
};

int main()
{
    D d;
    cout << d.num << endl;
    system("pause");
    return 0;
}

六、多态

 1)分类(2类)

①静态多态:函数重载以及运算符重载属于静态重载,复用函数名。

②动态多态:派生类和虚函数实现运行时多态。

2)动态多态代码实例

动态多态满足条件:①有继承关系;②子类重写父类虚函数。

class Father
  {
  public:
      void Face()
     {
         cout << "Father's face" << endl;
     }
 
     virtual void Say()
     {
         cout << "Father say hello" << endl;
     }
 };
 
 
 class Son:public Father
 {
 public:     
     void Say()
     {
         cout << "Son say hello" << endl;
     }
 };
 
 void main()
 {
     Son son;
     Father *pFather=&son; // 隐式类型转换
     pFather->Say();
 }

 3)纯虚函数和抽象类

①纯虚函数语法:virtual 返回类型 函数名(参数列表)=0

②抽象类概念:只要有一个纯虚函数,这个类就称为抽象类。

③抽象类特点:1)无法实例化对象;2)抽象类的子类必须要重写父类中的纯虚函数,否则也属于抽象类。

④代码实例:

class A
{
public :
    int num;
    //纯虚函数
    virtual void func() = 0;
};

class B :public A
{
public:
    virtual void func(){}
};

4)虚析构和纯虚析构

虚析构和纯虚析构可以解决父类指针释放子类对象时不干净的问题。

纯虚析构需要声明也需要实现。

 

原文地址:https://www.cnblogs.com/mango1997/p/14174173.html