C++类和对象(中)

简介: C++类和对象

C++类和对象(上):https://developer.aliyun.com/article/1459438


const 静态成员


对于一个类的成员,既要实现共享,又要实现不可变,那就用const修饰。定义静态const成员时,最好在类内部初始化。


this指针


C++封装性质:将数据和方法封装在一起

数据和方法是分开存储的。每个对象拥有独立的数据,但是对象方法是共享的。每个方法调用的时候都会隐式存在一个this指针。


C++规定:


  • this指针是隐含在成员函数内的一种指针,当一个对象被创建后,他的每一个成员函数都含有一个系统自动生成的隐含指针this,用来保存这个对象的地址。
  • 成员函数通过this指针即可知道操作的数据对象是谁
  • 隐藏在每一个非静态成员函数中,静态成员函数内部没有this指针,静态成员函数不能操作非静态成员变量


使用


  • 当形参和成员变量同名的时候可以使用this来区分。
  • 在类的普通成员函数中返回对象本身,可以使用 return *this;


const


const 修饰成员函数


使用const修饰成员函数时候,const修饰this指针指向的区域,成员函数体内不可以修改奔类中对的任何普通成员变量,放成员变量类型钱用mutable修饰时候例外。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
public:
    int getA() const {
        a = 50;
        return a;
    }
private:
    mutable int a{0};
};
int main(int argc, char* argv[])
{
    Person p;
    cout << p.getA() << endl;
    return 0;
}

函数如果不会修改成员函数的数据,就给函数加const。表示在此函数内不会给成员函数赋值。


const 修饰对象


const修饰对象叫常对象,编译器认为普通函数都有修改成员变量的可能。

因此不能调用非const修饰的函数。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
public:
    void setA(int a) const
    {
        // this->a = a;
    }
    int getA() const {
        a = 50;
        return a;
    }
private:
    mutable int a{0};
};
int main(int argc, char* argv[])
{
    const Person p;
    cout << p.getA() << endl;
    return 0;
}


友元


类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部访问。但是有时候需要在类的外部访问类的私有成员,怎么办?解决方法就是使用友元函数,友元函数是一种特权函数,C++允许这个特权函数访问私有成员。程序员可以把一个全局函数、某个类中的成员函数、甚至整个类声明为友元。


普通全局函数作为友元

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
    friend int getA(const Person &p);
public:
private:
    int a{0};
};
int getA(const Person &p)
{
    return p.a;
}
int main(int argc, char* argv[])
{
    const Person p;
    cout << getA(p) << endl;
    return 0;
}


类的成员函数作为另一个类的友元

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person;
class Dog
{
public:
    int getA(const Person &p);
};
class Person
{
    friend int Dog::getA(const Person &p);
public:
private:
    int a{0};
};
int Dog::getA(const Person &p)
{
    return p.a;
}
int main(int argc, char* argv[])
{
    const Person p;
    Dog d;
    cout << d.getA(p) << endl;
    return 0;
}


类作为另一个类的友元

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person;
class Dog
{
public:s
    int getA(const Person &p);
};
class Person
{
    friend class Dog;
public:
private:
    int a{0};
};
int Dog::getA(const Person &p)
{
    return p.a;
}
int main(int argc, char* argv[])
{
    const Person p;
    Dog d;
    cout << d.getA(p) << endl;
    return 0;
}


运算符的重载


运算符重载就是对已有的运算符重新进行定义,赋予其另外一种功能,以使用不同的数据类型。

运算符重载只是一种语法上的方便,也就是它只是另一种函数调用。

语法:函数名是由关键字operator紧跟着运算符

比如重载+ : operator+

注意:重载运算符不要更改运算符的本质。


可重载的运算符


几乎C中的所有运算符都可以重载,但是运算符重载的使用是相当受限制的。特别是不能使用C中当前没有意义的运算符,不能改变运算符的优先级,不能改变运算符的参数个数,这样的限制有意义,否则所有的这些行为产生的运算符只会混淆,而不是澄清寓意。

+ - * / % ^ & | ~ ! = < > += -= *= /= %=
^= &= |= << >> >>= <<= == != <= >= && || ++ -- ->* ' -> [] () new delete new[] delete[]

不能重载的运算符有: . :: .* ?: sizeof


运算符与友元函数

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
    friend ostream &operator<<(ostream &out, const Person &p);
    friend Person operator+(const Person p1, const Person &p2);
public:
private:
    int a{5};
};
ostream &operator<<(ostream &out, const Person &p)
{
    return out << p.a;
}
Person operator+(const Person p1, const Person &p2)
{
    Person p;
    p.a = p1.a + p2.a;
    return p;
}
int main(int argc, char* argv[])
{
    Person p;
    Person p2;
    cout << p << endl;
    cout << p2 + p << endl;
    return 0;
}


运算符与成员函数

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
    friend ostream &operator<<(ostream &out, const Person &p);
public:
    Person operator+(const Person &p2)
    {
        Person p;
        p.a = this->a + p2.a;
        return p;
    }

private:
    int a{5};
};
ostream &operator<<(ostream &out, const Person &p)
{
    return out << p.a;
}
int main(int argc, char* argv[])
{
    Person p;
    Person p2;
    cout << p << endl;
    cout << p2 + p << endl;
    return 0;
}


重载++/–


重载++和–运算符 需要区分是前置++/–还是后置++/–;区分方式是按照参数个数区分。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
    friend ostream &operator<<(ostream &out, const Person &p);
public:
    Person operator+(const Person &p2)
    {
        Person p;
        p.a = this->a + p2.a;
        return p;
    }
    Person operator++(int)
    {
        Person p = *this;
        this->a++;
        return p;
    }
    Person &operator++()
    {
        ++this->a;
        return *this;
    }
    Person operator--(int)
    {
        Person p = *this;
        this->a--;
        return p;
    }
    Person &operator--()
    {
        --this->a;
        return *this;
    }
private:
    int a{5};
};
ostream &operator<<(ostream &out, const Person &p)
{
    return out << p.a;
}
int main(int argc, char* argv[])
{
    Person p;
    Person p2;
    cout << p << endl;
    cout << p2 + p << endl;
    cout << p++ << endl;
    cout << p << endl;
    cout << ++p2 << endl;
    cout << p-- << endl;
    cout << p << endl;
    cout << --p2 << endl;
    return 0;
}


重载->和*

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
    friend ostream &operator<<(ostream &out, const Person &p);
public:
    Person()
    {
        cout << "Person" << endl;
    }
    ~Person()
    {
        cout << "~Person" << endl;
    }
    void show()
    {
        cout << "aaa" << endl;
    }
private:
    int a{5};
};
class SmartPoint
{
public:
    SmartPoint(Person *p)
    {
        this->p = p;
    }
    ~SmartPoint()
    {
        delete p;
    }
    Person* operator->()
    {
        return this->p;
    }
    Person& operator*()
    {
        return *this->p;
    }

private:
    Person *p;
};
ostream &operator<<(ostream &out, const Person &p)
{
    return out << p.a;
}
int main(int argc, char* argv[])
{
    SmartPoint point(new Person);
    point->show();
    (*point).show();
    return 0;
}


注意: 类中无指针成员的时候,无需重写=运算符,斗则必须重载=运算符


重载赋值运算符


指针作为类中的成员:

  • 拷贝构造函数必须自定义(默认拷贝构造是浅拷贝)
  • 必须重载=运算符(默认=运算符是浅拷贝)


如果不重写拷贝并且也不重载=运算符,编译器会给自动创建这些函数,默认的行为类似于浅拷贝。

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
class Person
{
    friend ostream &operator<<(ostream &out, const Person &p);
public:
    Person()
    {
        cout << "Person" << endl;
    }
    ~Person()
    {
        cout << "~Person" << endl;
    }
private:
    int a{5};
};
class SmartPoint
{
public:
    SmartPoint()
    {

    }
    SmartPoint(Person *p)
    {
        this->p = p;
    }
    ~SmartPoint()
    {
        delete p;
    }
    explicit SmartPoint(const SmartPoint &other)
    {
        p = new Person(*other.p);
    }
    SmartPoint& operator=(const SmartPoint &other)
    {
        cout << "赋值操作" << endl;
        p = new Person(*other.p);
        return *this;
    }
    Person *p;
};
ostream &operator<<(ostream &out, const Person &p)
{
    return out << p.a;
}
int main(int argc, char* argv[])
{
    SmartPoint point(new Person);
    SmartPoint point2(point);
    SmartPoint point3;
    point3 = point;
    cout << *(point.p) << endl;
    cout << *(point2.p) << endl;
    cout << *(point3.p) << endl;
    return 0;
}


重载 != 、 == 、 () 等运算符号

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
class Person
{
    friend ostream &operator<<(ostream &out, const Person &p);
public:
    Person(int a) : a(a) {}
    bool operator==(const Person &other)
    {
        return this->a == other.a;
    }
    bool operator!=(const Person &other)
    {
        return this->a != other.a;
    }
    Person & operator()(int a)
    {
        this->a += a;
        return *this;
    }
private:
    int a{5};
};
ostream &operator<<(ostream &out, const Person &p)
{
    return out << p.a;
}
int main(int argc, char* argv[])
{
    Person p(5);
    Person p2(6);
    Person p3(5);
    cout << (p==p2) << endl;
    cout << (p!=p3) << endl;
    Person p6(2);
    cout << p6(5) << endl;
    cout << Person(9)(5) << endl;
    return 0;
}

注意: 不要重载 && || ,因为用户无法实现 && 和 || 具有短路特性


总结: =,[],(),->操作符号只能通过成员函数进行重载,<< 和 >> 只能通过全局函数配合友元函数进行重载,不要重载&&和||操作符号


运算符 使用建议
所有一元运算符 成员
= () [] -> ->* 必须是成员
+= -= /= *= ^= &= != %= >>= <<= 成员
其他二元运算符 非成员


继承和派生


C++最终要的特性是代码重用,通过继承机制可以利用已有的数据类型来定义新的数据类型,新的类型不仅拥有旧类的成员,还拥有新定义的成员。一个B类继承自A类或者称为从A类派生出B类。这样的话A类称为基类(父类),B类称为派生类(子类),派生类中的成员包含两大部分:一类是从基类继承过来的,一类是自己增加的成员,从基类继承过来的表现其共性,而新增的成员体现了其个性。


子类继承于父类


父类派生出子类


继承的访问控制


代码示例:

class Derive : 继承方式 Base [, 继承方式 Base2 ... ]
{
// ...... 
}


继承方式分类:

访问权限:

  • public:公有继承
  • protected:保护继承
  • private:私有继承


父类个数:


单继承:指每个派生类只继承了一个基类的特征。

多继承:指多个基类派生出一个派生类的继承关系,多继承的派生类直接继承了不止一个基类的特性。

注意: 子类继承父类,子类拥有父类中全部成员变量和成员方法(除了构造和析构之外的成员方法),但是在派生类中,继承的成员并不一定能直接访问,不同的继承方式会导致不同的访问权限。


代码示例


public继承:在类外可以访问父类的public变量,在类内可以访问public和protected数据。私有数据在子类内外都是不可见的。

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
class Base
{
public:
    int a{1};
protected:
    int b{2};
private:
    int c{3};
};
class Derive : public Base
{
public:
    void show()
    {
        cout << a << b << endl;
    }
};
int main(int argc, char* argv[])
{
    Derive d;
    cout << d.a << endl;
    return 0;
}


protected继承:在类内可以访问public和protected数据。类外无法访问;private是不可见数据。

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
class Base
{
public:
    int a{1};
protected:
    int b{2};
private:
    int c{3};
};
class Derive : protected Base
{
public:
    void show()
    {
        cout << a << b << endl;
    }
};
int main(int argc, char* argv[])
{
    Derive d;
    return 0;
}


private继承:在类内可以访问public和protected数据。类外无法访问;private是不可见数据。

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
class Base
{
public:
    int a{1};
protected:
    int b{2};
private:
    int c{3};
};
class Derive : private Base
{
public:
    void show()
    {
        cout << a << b << endl;
    }
};
int main(int argc, char* argv[])
{
    Derive d;
    d.show();
    return 0;
}


继承中的构造和析构


子类是由父类成员叠加子类新成员而成。

构造顺序是 先父类再子类,析构顺序相反,如果有对象成员则是先父类再对象成员再子类,析构顺序相反。

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
#include <fstream>
using namespace std;
class Other
{
public:
    Other()
    {
        cout << "Other" << endl;
    }
    ~Other()
    {
        cout << "~Other" << endl;
    }
};
class Base
{
public:
    Base()
    {
        cout << "Base" << endl;
    }
    ~Base()
    {
        cout << "~Base" << endl;
    }
};
class Derive : private Base
{
public:
    Derive()
    {
        cout << "Derive" << endl;
    }
    ~Derive()
    {
        cout << "~Derive" << endl;
    }
    Other o;
};
int main(int argc, char* argv[])
{
    Derive d;
    return 0;
}


继承中的同名变量和同名函数


同名变量


当父类和子类中成员变量同名时,默认会选择子类成员,如果想访问父类同名成员则必须加上父类的作用域

#include <iostream>
#include <string>
#include <iostream>
#include <algorithm>
#include <fstream>
using namespace std;
class Base
{
public:
    int num{10};
};
class Derive : public Base
{
public:
    int num{20};
};
int main(int argc, char* argv[])
{
    Derive d;
    cout << d.num << endl;
    cout << d.Base::num << endl;
    return 0;
}


C++类和对象(下):https://developer.aliyun.com/article/1459443

目录
相关文章
|
24天前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
3天前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
31 16
|
7天前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
46 6
|
27天前
|
编译器 C语言 C++
类和对象的简述(c++篇)
类和对象的简述(c++篇)
|
24天前
|
安全 编译器 C语言
【C++篇】深度解析类与对象(中)
在上一篇博客中,我们学习了C++类与对象的基础内容。这一次,我们将深入探讨C++类的关键特性,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载、以及取地址运算符的重载。这些内容是理解面向对象编程的关键,也帮助我们更好地掌握C++内存管理的细节和编码的高级技巧。
|
24天前
|
存储 程序员 C语言
【C++篇】深度解析类与对象(上)
在C++中,类和对象是面向对象编程的基础组成部分。通过类,程序员可以对现实世界的实体进行模拟和抽象。类的基本概念包括成员变量、成员函数、访问控制等。本篇博客将介绍C++类与对象的基础知识,为后续学习打下良好的基础。
|
2月前
|
C++ 芯片
【C++面向对象——类与对象】Computer类(头歌实践教学平台习题)【合集】
声明一个简单的Computer类,含有数据成员芯片(cpu)、内存(ram)、光驱(cdrom)等等,以及两个公有成员函数run、stop。只能在类的内部访问。这是一种数据隐藏的机制,用于保护类的数据不被外部随意修改。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。成员可以在派生类(继承该类的子类)中访问。成员,在类的外部不能直接访问。可以在类的外部直接访问。为了完成本关任务,你需要掌握。
84 19
|
2月前
|
存储 编译器 数据安全/隐私保护
【C++面向对象——类与对象】CPU类(头歌实践教学平台习题)【合集】
声明一个CPU类,包含等级(rank)、频率(frequency)、电压(voltage)等属性,以及两个公有成员函数run、stop。根据提示,在右侧编辑器补充代码,平台会对你编写的代码进行测试。​ 相关知识 类的声明和使用。 类的声明和对象的声明。 构造函数和析构函数的执行。 一、类的声明和使用 1.类的声明基础 在C++中,类是创建对象的蓝图。类的声明定义了类的成员,包括数据成员(变量)和成员函数(方法)。一个简单的类声明示例如下: classMyClass{ public: int
78 13
|
2月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
68 5
|
2月前
|
存储 算法 搜索推荐
【C++面向对象——群体类和群体数据的组织】实现含排序功能的数组类(头歌实践教学平台习题)【合集】
1. **相关排序和查找算法的原理**:介绍直接插入排序、直接选择排序、冒泡排序和顺序查找的基本原理及其实现代码。 2. **C++ 类与成员函数的定义**:讲解如何定义`Array`类,包括类的声明和实现,以及成员函数的定义与调用。 3. **数组作为类的成员变量的处理**:探讨内存管理和正确访问数组元素的方法,确保在类中正确使用动态分配的数组。 4. **函数参数传递与返回值处理**:解释排序和查找函数的参数传递方式及返回值处理,确保函数功能正确实现。 通过掌握这些知识,可以顺利地将排序和查找算法封装到`Array`类中,并进行测试验证。编程要求是在右侧编辑器补充代码以实现三种排序算法
52 5