2024-01-03 c++

本文详细解释了C++中虚函数指针、基类和派生类的关系,以及对象友元函数的作用,探讨了访问修饰符、继承下的成员访问权限,以及如何通过基类指针实现多态和动态绑定。还介绍了友元函数如何处理私有成员的访问问题,并提供了一个实例以说明这些概念的实际应用。
摘要由CSDN通过智能技术生成
  • 虚函数 指针和对象
  • 友元函数 求2点距离

🐟 指针和对象的关系
由于基类和派生类的特殊关系,基类指针和派生类指针与基类对象和派生类对象4种可能:

  • 直接用基类指针引用基类对象
  • 直接用派生类指针引用派生类对象
  • 用基类指针引用一个派生类对象
  • 用派生类指针引用一个基类对象

派生类指针只有经过强制类型转换才能引用基类对象

🐟 类指针的关系

class A {
	...
};
class B : public A { //B是A的派生类
	...
};
	A *P; //指向类型A的对象的指针
	A A_obj; //类型A的对象
	B A_obj; //类型B的对象
	p=& A_obj; //p指向类型A的对象
	p=& B_obj; //p指向类型B的对象

利用p,可以通过B_obj访问所有从A类继承的元素,但不能用p访问B类自定义的元素(除非用了显示类型转换)

🐕 eg

类指针是指向类对象的指针,它用于访问类的成员和方法。类指针的关系取决于它所指向的类的继承关系。如果一个类指针指向一个基类对象,那么它可以通过该指针访问基类的成员和方法;如果一个类指针指向一个派生类对象,那么它可以通过该指针访问派生类和基类的成员和方法。类指针的关系也可以用于多态性,即通过基类指针来调用派生类的方法。因此,类指针的关系可以用于实现多态性和动态绑定。

当我们有一个基类Animal和一个派生类Dog时,我们可以使用类指针来访问它们的成员和方法。

#include <iostream>
using namespace std;

class Animal {
public:
    virtual void makeSound() {
        cout << "Animal makes a sound" << endl;
    }
};

class Dog : public Animal {
public:
    void makeSound() {
        cout << "Dog barks" << endl;
    }
};

int main() {
    Animal* animalPtr;
    Dog dog;

    animalPtr = &dog; // 将派生类对象的地址赋给基类指针

    animalPtr->makeSound(); // 通过基类指针调用派生类的方法
    return 0;
}

在这个例子中,我们定义了一个基类Animal和一个派生类Dog。在main函数中,我们创建了一个基类指针animalPtr,并将派生类对象的地址赋给它。然后,我们通过基类指针调用makeSound方法,这将调用派生类Dog的makeSound方法。这个例子展示了类指针的多态性和动态绑定的特性。

在C++中,基类和派生类之间的访问权限是由访问修饰符控制的。访问修饰符包括public、protected和private。

  • public:基类中的public成员在派生类中仍然是public的,可以被派生类的对象和函数访问。
  • protected:基类中的protected成员在派生类中仍然是protected的,只能被派生类的对象和函数访问。
  • private:基类中的private成员在派生类中是不可访问的,即使是派生类的成员函数也无法访问。

下面是一个示例,演示了基类和派生类之间的访问权限:

#include <iostream>
using namespace std;

class Animal {
public:
    int publicVar;
protected:
    int protectedVar;
private:
    int privateVar;
};

class Dog : public Animal {
public:
    void accessBaseMembers() {
        publicVar = 1; // 可以访问基类的public成员
        protectedVar = 2; // 可以访问基类的protected成员
        // privateVar = 3; 无法访问基类的private成员
    }
};

int main() {
    Dog dog;
    dog.publicVar = 1; // 可以访问基类的public成员
    // dog.protectedVar = 2; 无法直接访问基类的protected成员
    // dog.privateVar = 3; 无法直接访问基类的private成员
    return 0;
}

在这个例子中,派生类Dog可以访问基类Animal中的public和protected成员,但无法直接访问private成员。

在C++中,基类对派生类的访问权限是由派生类的访问修饰符控制的。具体来说:

  1. 如果派生类中的成员函数是public的,那么基类可以访问派生类中的public和protected成员,但无法直接访问private成员。
  2. 如果派生类中的成员函数是protected的,那么基类可以访问派生类中的public和protected成员,但无法直接访问private成员。
  3. 如果派生类中的成员函数是private的,那么基类无法访问派生类中的任何成员。

下面是一个示例,演示了基类对派生类成员的访问权限:

#include <iostream>
using namespace std;

class Animal {
public:
    void accessDerivedMembers() {
        Dog dog;
        dog.publicVar = 1; // 可以访问派生类的public成员
        dog.protectedVar = 2; // 可以访问派生类的protected成员
        // dog.privateVar = 3; 无法直接访问派生类的private成员
    }
};

class Dog : public Animal {
public:
    int publicVar;
protected:
    int protectedVar;
private:
    int privateVar;
};

int main() {
    Animal animal;
    animal.accessDerivedMembers(); // 基类调用派生类的成员函数
    return 0;
}

在这个例子中,基类Animal可以通过成员函数accessDerivedMembers访问派生类Dog中的public和protected成员,但无法直接访问private成员。

在C++中,访问修饰符有显式和隐式之分。这指的是在派生类中继承基类成员时,基类成员的访问权限可能会发生变化。

  1. 显式继承:当派生类中使用访问修饰符明确指定继承的基类成员的访问权限时,这被称为显式继承。例如:

    class Base {
    public:
        int publicVar;
    protected:
        int protectedVar;
    private:
        int privateVar;
    };
    
    class Derived : public Base {
    public:
        using Base::publicVar; // 显式将基类的public成员设为public
        using Base::protectedVar; // 显式将基类的protected成员设为protected
    };
    
  2. 隐式继承:如果派生类中没有显式地指定继承的基类成员的访问权限,那么基类成员的访问权限将保持不变。这被称为隐式继承。例如:

    class Base {
    public:
        int publicVar;
    protected:
        int protectedVar;
    private:
        int privateVar;
    };
    
    class Derived : public Base {
        // 没有显式指定基类成员的访问权限
    };
    

在隐式继承中,基类的public成员仍然是public的,protected成员仍然是protected的,private成员仍然是不可访问的。

在C++中,即使进行了显示类型转换,基类仍然无法直接访问派生类的private成员。这是因为private成员对于派生类以外的类都是不可访问的,无论是基类还是其他类。

在C++中,可以使用友元函数或友元类来实现对私有成员的访问。如果你希望基类能够访问派生类的私有成员,可以考虑使用友元函数或友元类的方法。

下面是一个示例,演示了如何使用友元函数来实现基类对派生类私有成员的访问:

#include <iostream>
using namespace std;

class Dog;

class Animal {
public:
    void accessDogPrivateMember(Dog& dog); // 友元函数声明
};

class Dog : public Animal {
private:
    int privateVar;

    friend void Animal::accessDogPrivateMember(Dog& dog); // Animal中的成员函数作为友元函数

public:
    Dog() : privateVar(42) {}

    void showPrivateVar() {
        cout << "Dog's privateVar: " << privateVar << endl;
    }
};

void Animal::accessDogPrivateMember(Dog& dog) {
    cout << "Animal accessing Dog's privateVar: " << dog.privateVar << endl;
}

int main() {
    Dog dog;
    Animal animal;
    animal.accessDogPrivateMember(dog); // 基类调用友元函数访问派生类的私有成员
    return 0;
}

在这个例子中,Animal类中的成员函数accessDogPrivateMember被声明为友元函数,这使得它可以访问Dog类中的private成员privateVar。通过这种方式,基类Animal可以访问派生类Dog的私有成员。


🐱 项目小例子

新建空项目

求2点距离

#include<iostream>
#include<math.h>
using namespace std;
class Point {
public:
	Point(double xi, double yi) { X = xi; Y = yi; }
	double GetX() { return X; }
	double GetY() { return Y; }
	friend double Distance(Point& a, Point& b);// Distance是Point的友员
private:double X, Y;
};
double Distance(Point& a, Point& b) { //2点距离
	double dx = a.X - b.X; double dy = a.Y - b.Y;
	return sqrt(dx*dx + dy*dy);
}
int main()
{
	Point p1(3.0, 5.0), p2(4.0, 6.0);
	double d = Distance(p1, p2);
	cout << "distance = " << d << endl; // distance = 1.41421
	system("pause");
	return 0;
}

姓名年级

在这里插入图片描述

#ifndef PERSON_H_
#define PERSON_H_
class Person { //父类
private:
	char name[20];
public:
	Person();
	void showName();
	void setName(char *s);
};
class Student :public Person { //子类
private:
	char grade[20];
public:
	void setGrade(char *g);
	void showGrade();
};
#endif
//虚函数 类指针 指针和对象
#include<iostream>
#include<math.h>
#include<string>
using namespace std;
Person::Person() {}
void Person::setName(char *s) {
	strcpy_s(name, s);
}
void Person::showName() {
	cout << "name:" << name << endl;
}
void Student::setGrade(char *g) {
	strcpy_s(grade, g);
}
void Student::showGrade() {
	cout << "grade:" << grade << endl;
}
int main()
{
	Person *p;
	Person p1;
	Student s;
	p = &p1; //基类指针指向基类对象
	p->setName("mao");
	p->showName();
	p = &s; //基类指针指向派生类对象
	p->setName("yu");
	p->showName();
	s.setGrade("1");
	((Student*)p)->showGrade(); //基类指针强转换成派生指针
	system("pause");
	return 0;
}

年月日时分秒

在这里插入图片描述

#ifndef DATE_H_
#define DATE_H_
class Date {
protected:
	int year, month, day;
public:
	Date();
	Date(int y, int m, int d);
	virtual void print(); // 声明为虚函数
	void setDate(int y, int m, int d);
};

class DateTime : public Date {
protected:
	int hours, minutes, seconds;
public:
	DateTime(int y, int m, int d, int h, int mm, int s);
	void print() override; // 声明为覆盖基类的虚函数
	void setTime(int h, int mm, int s); // 修改函数名为setTime
};
#endif
#include<iostream>
#include<math.h>
#include<string>
using namespace std;

Date::Date() {}
Date::Date(int y, int m, int d) {
	year = y; month = m; day = d;
}
void Date::setDate(int y, int m, int d) {
	year = y; month = m; day = d;
}
void Date::print() {
	cout << year << "-" << month << "-" << day<<" ";
}

DateTime::DateTime(int y, int m, int d, int h, int mm, int s) : Date(y, m, d) {
	hours = h; minutes = mm; seconds = s;
}
void DateTime::setTime(int h, int mm, int s) {
	hours = h; minutes = mm; seconds = s;
}
void DateTime::print() {
	//((Date*)this)->print(); // 对this指针作类型转换
	/*-----
	0x00D72739 处有未经处理的异常(在 Project2.exe 中): 0xC00000FD: Stack overflow (参数: 0x00000001, 0x002D2F54)。

	这种异常通常是由于递归调用或无限循环导致的栈溢出。
	-----*/
	Date::print(); // 直接调用基类的print函数
	cout << hours << ":" << minutes << ":" << seconds << endl;
}

int main() {
	DateTime dt(2024, 1, 3, 1, 21, 0);
	dt.print(); //2024-1-3 1:21:0
	system("pause");
	return 0;
}

0x00D72739 处有未经处理的异常(在 Project2.exe 中): 0xC00000FD: Stack overflow (参数: 0x00000001, 0x002D2F54)。

这种异常通常是由于递归调用或无限循环导致的栈溢出。在你的代码中,使用((Date*)this)->print(); 可能会导致无限递归调用,从而导致栈溢出异常。

为了避免这个问题,你可以直接调用基类的print()函数,而不是通过this指针进行转换。以下是修改后的DateTime类的print()函数:

void DateTime::print() {
    Date::print(); // 直接调用基类的print函数
    cout << hours << ":" << minutes << ":" << seconds << endl;
}

通过直接调用基类的print()函数,你可以避免潜在的无限递归调用,从而解决栈溢出异常的问题。

希望这可以帮助你解决异常问题。

  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值