- 虚函数 指针和对象
- 友元函数 求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++中,基类对派生类的访问权限是由派生类的访问修饰符控制的。具体来说:
- 如果派生类中的成员函数是public的,那么基类可以访问派生类中的public和protected成员,但无法直接访问private成员。
- 如果派生类中的成员函数是protected的,那么基类可以访问派生类中的public和protected成员,但无法直接访问private成员。
- 如果派生类中的成员函数是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++中,访问修饰符有显式和隐式之分。这指的是在派生类中继承基类成员时,基类成员的访问权限可能会发生变化。
-
显式继承:当派生类中使用访问修饰符明确指定继承的基类成员的访问权限时,这被称为显式继承。例如:
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 };
-
隐式继承:如果派生类中没有显式地指定继承的基类成员的访问权限,那么基类成员的访问权限将保持不变。这被称为隐式继承。例如:
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()函数,你可以避免潜在的无限递归调用,从而解决栈溢出异常的问题。
希望这可以帮助你解决异常问题。