面向对象基础常识

2168 字
11 分钟
面向对象基础常识

C++ 面向对象基础#

访问权限#

权限类自身(成员/友元)派生类(成员/友元)外部/普通用户
public
protected
private

虚析构#

delete p 只会删除静态类型,如果 p 指向 Base 这个基类,那么 delete 无法删除它的派生类 Derived,比如 Base* p = new Derived(); , 但使用虚析构就可以了: virtual ~Base();

class Animal {
public:
~Animal() { cout << "~Animal\n"; }
};
class Dog : public Animal {
char* name_;
public:
Dog(const char* s) {
name_ = new char[20];
cout << "Dog born\n";
}
~Dog() {
delete[] name_;
cout << "~Dog\n";
}
};
int main() {
Animal* p = new Dog("Tom");
delete p;
return 0;
}

派生类权限:#

分两步看:基类成员的原始权限 + 继承方式,取更严格的那个。

基类权限public 继承protected 继承private 继承
publicpublicprotectedprivate
protectedprotectedprotectedprivate
private不可直接访问不可直接访问不可直接访问
class Base {
public: int a;
protected: int b;
private: int c; // 派生类永远不能直接碰
};
class Pub : public Base {
// a: public, b: protected, c: 不可访问
};
class Pro : protected Base {
// a: protected, b: protected, c: 不可访问
};
class Pri : private Base {
// a: private, b: private, c: 不可访问
};

上面的权限指的是”对外展示的权限”,而不是派生类”内部访问的权限”。

  • 内部访问:只看基类自己的权限标签(public/protected/private),不管你怎么继承。
  • 外部访问:才看继承方式,那是”降级规则”——把基类成员的对外可见度往下压一级或两级。

友元 & Tips#

  • 友元关系不能继承、不能交换也不能传递
  • 友元函数不是成员函数,只是一个类对一个函数的访问授权。
  • 友元函数是 private 权限。
  • private 是本类成员函数可访问,protected 是本类和派生类,public 是任何地方都可以访问。
  • 类内定义的函数默认是内联函数,但是类内声明但是在类外定义的函数要加上 inline 关键字。
  • inline 用于实现而非用于声明。
  • 析构和构造函数都必须在类内声明,但可以在类外定义。
  • delete 删除一个动态对象,会先析构,再释放其占用的内存。
  • 类的定义体中包括:数据成员、成员函数和成员的访问控制信息。
  • 静态成员数据由该类的所以实例化对象共享,其生命周期是整个程序周期。
  • ij 声明在 Apublic: 之前,默认就是 private。到了 B::make() 里想直接访问,就碰壁了。
class A {
int i, j;
public:
void get();
};
class B : public A {
int k;
public:
void make();
};
void B::make() {
k = i * j;
}
  • 类内可以定义赋值语句,但是不能有执行语句,比如:
class B {
int k;
int z = 42;
int z = x * y;
k = 1; // false
};
  • 虚析构函数的作用是避免在 delete 基类指针时,只析构基类部分,漏掉派生类部分,造成资源泄露。
  • 语法上不存在虚构造函数,但可以通过虚函数(如 clone())实现类似效果。
class Base {
public:
virtual ~Base() {}
virtual Base* clone() const = 0; // 虚函数模拟"虚构造"
};
class Derived : public Base {
public:
Derived* clone() const override {
return new Derived(*this);
}
};
  • 纯虚函数是一个在基类中说明虚函数,它在该基类中没有定义,但要求任何派生类中的可访问性的规定是相同的。

静态 & 常#

  • 静态成员函数只能访问静态成员变量,且它属于整个类,可以用 static void show()Student::show() 也可以通过对象访问普通成员变量,比如:
class A {
public:
int x;
static void set(A &a) {
a.x = 100;
}
};
int main() {
A obj;
A::set(obj);
cout << obj.x << endl;
}
  • 由于数据隐藏的必要,静态成员数据通常为 private(私有成员)。
  • X a[3] *p[2] 里面实际上只调用了 3 次构造函数,因为 X 类的指针类型数组没有实例化对象,无需调用构造函数。
  • int *p = new A | new A[10] | new int[3]{1,2,3} | int[5]{} (初始化为0)
  • 常成员函数案例:void show() const
  • 常对象只能调用常成员函数,不能修改成员变量。

多态#

静态多态:编译时#

包括函数重载和运算符重载

// 函数重载
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
// 模板
template<typename T>
T max(T a, T b) { return a > b ? a : b; }

运算符重载:复数相加

class Complex {
double real, imag;
public:
Complex(double r = 0, double i = 0) {
real = r;
imag = i;
}
virtual ~Complex() {
cout << "Complex deleted" << endl;
}
// getter 函数,让外部能读取私有成员
double getReal() const { return real; }
double getImag() const { return imag; }
// 声明为友元函数,使其可以访问私有成员
friend Complex operator+(const Complex& om1, const Complex& om2);
friend Complex operator+(const int il, const Complex& om1);
};
Complex operator+(const Complex& om1, const Complex& om2) {
Complex temp;
temp.real = om1.real + om2.real;
temp.imag = om1.imag + om2.imag;
return temp;
}
Complex operator+(const int il, const Complex& om1) {
Complex temp;
temp.real = om1.real + il;
temp.imag = om1.imag;
return temp;
}
int main() {
Complex a(5.2, -1.3);
Complex b(1.1, 0.3);
Complex c = a + b;
cout << c.getReal() << " " << c.getImag() << endl;
Complex d = operator+(1, a);
cout << d.getReal() << " " << d.getImag() << endl;
}

运算符重载函数

  • 成员运算符函数
  • 友元函数重载
  • 双目运算符函数
  • 单目运算符函数

Tips

  • 不能用友元函数重载的运算符是:= | () | [ ] | -> 其余都可以,因为这些运算符必须是非静态成员函数,这是C++标准的硬性规定。
  • = 可以被重载,但是重载了的= 函数不能被继承。(不能继承实际是被系统缺省定义替换掉了)
  • 每一个类对象实例在创建的时候,如果用户没有定义“赋值运算符重载函数”编译器会自动生成一个隐含和默认的“赋值运算符重载函数”。
  • = 以外的所有基类运算符都会被自动继承。

类的 6 个默认成员函数

分类默认成员函数作用
初始化构造函数对象初始化
销毁析构函数对象销毁
拷贝拷贝构造函数创建对象时复制数据
赋值赋值运算符重载(operator=)对已有对象赋值
取地址operator&获取普通对象地址
取地址const operator&获取 const 对象地址

重载 = 和 <<

// 重载 =
class Complex {
double real, imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {};
Complex& operator=(const Complex& c) {
if (this == &c) { return *this; }
real = c.real;
imag = c.imag;
return *this;
}
friend ostream& operator<<(ostream& os, const Complex& c);
};
ostream& operator<<(ostream& os, const Complex& c) {
os << "(" << c.real << " + " << c.imag << "i)";
return os;
}
// 重载 <<
class Complex {
double real, imag;
public:
Complex(double r, double i) : real(r), imag(i) {};
friend ostream& operator<<(ostream& os, const Complex& c);
};
ostream& operator<<(ostream& os, const Complex& c) {
os << c.real << " + " << c.imag << "i";
return os;
}

重载 圆括号 和 中括号(必须用成员函数)

class Array {
int data[10];
public:
Array() {
for (int i = 0; i < 10; i++) { data[i] = i; }
}
int& operator[](int i) {
return data[i];
}
int operator()(int i) {
return data[i] * data[i];
}
};

动态多态:运行时#

class Animal {
public:
virtual void speak() = 0; // 虚函数
};
class Dog : public Animal {
public:
void speak() override { cout << "wang wang"; }
};
Animal* p = new Dog();
p->speak(); // 运行时通过虚表找到 Dog::speak,输出"wang wang"

异常#

double divide(double a, double b) {
if (b == 0) throw runtime_error("can not be 0");
return a / b;
}
int main() {
try {
cout << divide(10, 0);
} catch (const runtime_error e) {
cerr << "err: " << e.what() << endl;
}
}

模板#

#include <bits/stdc++.h>
using namespace std;
template <class AT>
AT max(AT x,AT y){return (x>y)?x:y;}
int main(){
int x1=1,y1=2;
cout<<max(x1,y1)<<endl;
float x3=1.1,y3=2.2;
cout<<max(x3,y3)<<endl;
char x4='a',y4='b';
cout<<max(x4,y4)<<endl;
int x2 = 3;
char y2 = 4;
cout << max(x2, y2) << endl;
}

C++ 函数调用的一般顺序 C++中函数调用的一般顺序为: (1)寻找一个参数完全匹配的函数,若找到则调用之,否则:(2)寻找一个函数模板,若找到则将其实例化为一个模板函数,然后调用之,否则: (3)寻找重载函数,考察有无可通过产生参数匹配的函数,若有则调用之。

类模板和模板类的区别

template<typename T>
class Stack { // ← 这是"类模板"
T* data;
void push(const T&);
};
Stack<int> s; // ← Stack<int> 是一个"模板类"(实例化后的具体类)
Stack<double> d; // ← Stack<double> 是另一个"模板类"

输入输出#

ios_base
└── ios
├── ostream ← 输出流
│ └── ofstream (文件输出)
│ └── ostringstream (字符串输出)
├── istream ← 输入流
└── iostream ← 双向流(同时继承 istream 和 ostream)

缺省#

class A {};
// 等价于
class A : private B {};
struct A {};
// 等价于
struct A : public B {};
面向对象基础常识
https://dongyanzhang.com/posts/cpp/cpp-oop-basics/
作者
阿东阿言
发布于
2026-07-12
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
阿东阿言
I share my notes | projects | experience here...
公告
欢迎来到阿东阿言的博客
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
25
分类
3
标签
5
总字数
11,186
运行时长
0
最后活动
0 天前
站点信息
构建平台
Local
博客版本
v1.0.0
文章许可
CC BY-NC-SA 4.0