C++面向对象程序设计笔记
对象指针
指向对象的指针
类名 *指针变量名
Time *pt;
Time t1;
pt = &t1;
*pt
表示pt
指向的对象,即t1
(*pt).get_time()
<=>pt->get_time()
指向对象成员的指针
对象中的成员也有地址,存放对象成员地址的指针变量就是指向对象成员的指针变量
int *p1;
p1 = &t1.hour; // 将对象t1的数据成员hour的地址给p1,使p1指向t1.hour
指向对象成员函数的指针
与普通函数最大的区别与他是类中的成员
数据类型名 (类名::*指针变量名)(参数列表);
void (Time::*p2)();
p2 = &Time::get_time;
(t1.*p2)();
不同的对象都调用同一个函数的目标代码
指针当前对象的this指针
他是指向本类对象的指针,他的值是当前被调用的成员函数所在的对象的起始地址
常对象
防止普通成员函数修改对象中数据的值
类名 const 对象名[(实参表)];
如果一个对象被声明为常对象,则通过该对象只能调用它的常成员函数
常对象成员
const int hour;
只能通过构造函数的参数初始化表对常数据成员进行初始化
Time::Time(int h):hour(h){}
常成员函数
void get_time() const;
对象的赋值与复制
对象的赋值
- 只对其中的数据成员赋值,不对成员函数赋值
- 类的数据成员不能包括动态分配的数据,否则在赋值时可能出现严重后果
Student stud1,stud2;
.
.
.
stud2 = stu1;
对象的复制
触发拷贝构造函数
Box box2(box1);
Box::Box(const Box& b){
height = b.height;
width = b.width;
length = b.length;
}
不同对象间实现数据共享
把数据成员定义成静态
定义静态变量后的成员和函数是类的一部分而不是对象的一部分
static int height;
.
.
.
Box::height = 10; // 初始化
只在类体中声明静态数据成员时加static,不必在初始化语句中加static
不能用构造函数的参数初始化表对静态数据成员进行初始化
在类外可以通过与对象引用公用的静态数据成员(a.height),也可以通过类名引用静态数据成员(Box::height)
用静态成员函数访问静态数据成员
static int volumn();
静态成员函数的作用是为了能处理静态数据成员
静态成员函数没有this指针
友元(friend)
友元函数
#include <iostream>
using namespace std;
class Time {
private:
int hour;
int minute;
int sec;
public:
Time(int h, int m, int s):hour(h), minute(m), sec(s) {}
friend void display(Time &);
};
void display(Time &t){
cout << t.hour << ":" << t.minute << ":" << t.sec << endl;
}
int main() {
Time t1(10,13,56);
display(t1);
return 0;
}
#include <iostream>
using namespace std;
class Date; // 提前引用声明
class Time {
private:
int hour;
int minute;
int sec;
public:
Time(int h, int m, int s):hour(h), minute(m), sec(s) {}
void display(Date &);
};
class Date {
public:
Date(int m, int d, int y):month(m), day(d), year(y){}
friend void Time::display(Date &); // 这个函数可以使用我的private
private:
int month;
int day;
int year;
};
void Time::display(Date &d){
cout << d.month << '/' << d.day << '/' << d.year << endl;
cout << hour << ':' << minute << ':' << sec << endl;
}
int main() {
Time t1(10,13,56);
Date d1(12,25,2004);
t1.display(d1);
return 0;
}
友元类
友元类(friend class)是C++中的一个特性,它允许非成员函数访问类的私有(private)和保护(protected)成员。此外,一个类可以声明另一个类为其友元类,这样后者的所有成员函数都能访问前者的私有和保护成员。
#include <iostream>
#include <string>
using namespace std;
// 声明一个类,它将拥有私有数据成员
class Person {
private:
string name;
int age;
public:
Person(const string& name, int age) : name(name), age(age) {}
// 声明友元类
friend class FriendClass; // 允许FriendClass访问Person的私有成员
};
// 定义友元类
class FriendClass {
public:
// 友元类的成员函数,可以访问Person类的私有成员
void printPersonInfo(const Person& person) {
cout << "Name: " << person.name << ", Age: " << person.age << endl;
}
};
int main() {
// 创建Person对象
Person person("Alice", 30);
// 创建FriendClass对象
FriendClass friendObj;
// 友元类的成员函数可以访问Person的私有成员
friendObj.printPersonInfo(person);
return 0;
}
类模板
对于功能相同二数据类型不同的一些函数,不必一一定义各个函数,可以定义一个对任何类型变量进行操作的函数模板,调用模板时,系统会根据实参的类型,取代函数模板中的类型参数,得到具体的我函数
template<class numtype>
class Compare {
private:
numtype x, y;
public:
Compare(numtype a, numtype b):x(a), y(b){}
numtype max(){
return (x > y) ? x : y;
}
numtype min(){
return (x < y) ? x : y;
}
};
Compare<int> cmp(4,7);
对运算符进行重载
1. 把运算重载的函数作为类的成员函数
函数类型 operator运算符名字(形参表){
对运算符的重载处理
}
Complex operator+(Complex& c1, Complex& c2);
#include<iostream>
using namespace std;
class Complex {
private:
double real;
double imag;
public:
Complex():real(0), imag(0){}
Complex(double r, double i):real(r), imag(i){}
Complex operator+(Complex &);
void display();
};
// low手写法
Complex Complex::operator+(Complex &c2){
Complex c;
c.real = this->real + c2.real;
c.imag = this->imag + c2.imag;
return c;
}
// 高手写法
Complex Complex::operator+(Complex &c2){
return Complex(this->real + c2.real, this->imag + c2.imag);
}
void Complex::display(){
cout << "(" << real << "," << imag << "i)" << endl;
}
int main(){
Complex c1(3,4), c2(5,-10), c3;
c3 = c1 + c2 + c2;
c3.display();
return 0;
}
c1+c2
解释成c1.operator+(c2)
运算符被重载后,其原有的功能仍然保留
不能重载的运算符:
.
*
::
sizeof
?:
重载不能改变运算符运算对象的个数(双目重载后也是双目)
重载不能改变运算符优先级
重载运算符的函数不能有默认参数
重载运算符必须和用户定义的自定义类型的对象一起使用,起参数至少应有一个是类对象(或类对象的引用)
用于类对象的运算符一般必须重载,
=
和&
是例外理论上,可以将一个运算符重载为执行任意的操作
2. 运算符重载函数不是类的成员函数,在类中声明为友元函数
不能做连加运算
#include<iostream>
using namespace std;
class Complex {
private:
double real;
double imag;
public:
Complex():real(0), imag(0){}
Complex(double r, double i):real(r), imag(i){}
friend Complex operator+(Complex &, Complex &);
void display();
};
Complex operator+(Complex &c1, Complex &c2){
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
void Complex::display(){
cout << "(" << real << "," << imag << "i)" << endl;
}
int main(){
Complex c1(3,4), c2(5,-10), c3;
c3 = c1 + c2;
c3.display();
return 0;
}
c1+c2
解释成operator+(c1, c2)
一般把单目运算符重载为成员函数
一般把双目运算符重载为友元函数
重载单目运算符
#include<iostream>
using namespace std;
class Time {
private:
int minute;
int sec;
public:
Time():minute(0), sec(0) {}
Time(int m, int s):minute(m), sec(s) {}
Time operator++(); // ++a
Time operator++(int); // a++
void display(){ cout << minute << ":" << sec << endl; }
};
Time Time::operator++(){
sec++;
if(sec >= 60){
sec -= 60;
minute++;
}
return *this;
}
Time Time::operator++(int){
Time temp(*this); // 创建一临时变量用于返回还没自增的
sec++;
if(sec >= 60){
sec -= 60;
minute++;
}
return temp;
}
int main(){
Time time1(34,59),time2;
cout << "time1 : ";
time1.display();
++time1;
cout << "++time1 : ";
time1.display();
time2 = time1++;
cout << "time1++ : ";
time1.display();
cout << "time2 : ";
time2.display();
return 0;
}
运行结果
time1 : 34:59 ++time1 : 35:0 time1++ : 35:1 time2 : 35:0
重载流插入运算符
// 类内部
friend ostream& operator<<(ostream&, Complex&);
// 类外部
ostream& operator<<(ostream &output, Complex &c){
output << "(" << c.real << "+" << c.imag << "i)";
return output;
}
// main内部
cout << c3;
重载流提取运算符
// 类内部
friend istream& operator>>(istream&, Complex&);
// 类外部
istream& operator>>(istream &input, Complex &c){
input >> c.real >> c.imag;
return input;
}
// main内部
cin >> c1;
类的继承
继承就是在已存在的类的基础上建立一个新的类。已存在的类叫做“基类
”或“父类
”,新建的类称为“派生类
”或“子类
”。派生类是基类的具体化,而基类则是派生类的抽象。
派生类的声明方式
class 派生类名:[继承方法] 基类名{
派生类新增的成员
}
继承方法:
public
(公用继承),private
(私有继承),protected
(受保护的继承), 默认private
派生类的构成
- 从基类接受: 派生类把基类的全部成员没有选择的接受过来(但不包括构造和析构)
- 调整从基类接受的成员: 可以通过继承把基类的公用成员指定为在派生类的访问属性为私有.可以在派生类中声明一个与基类成员同名的成员,新成员会覆盖基类的同名成员
- 增加的成员: 体现派生类对基类功能的拓展
公用继承
基类的公有成员和受保护成员在派生类保持原有的访问属性,基类的私有成员并没有成为派生类的私有成员,仍然时基类的私有成员
在基类的访问属性 | 继承方式 | 在派生类的访问属性 |
---|---|---|
private | public | 不可访问 |
public | public | public |
protected | public | protected |
#include<iostream>
#include<string>
using namespace std;
class Student {
private:
int num;
string name;
char sex;
public:
void get_value(){
cin >> num >> name >> sex;
}
void display(){
cout << num << endl;
cout << name << endl;
cout << sex << endl;
}
};
class Student1:public Student {
private:
int age;
string addr;
public:
void get_value_1(){
cin >> age >> addr;
}
void display_1(){
// cout << num << endl; // 不允许直接访问基类的private
// cout << name << endl;
// cout << sex << endl;
display(); // 允许访问基类的公用成员函数来访问基类的private
cout << age << endl;
cout << addr << endl;
}
};
int main(){
Student1 stud;
stud.get_value_1();
stud.get_value();
stud.display_1();
return 0;
}
私有继承
基类的公有成员和保护成员在派生类中成了私有成员,基类的私有成员并没有成为派生类的私有成员,仍然时基类的私有成员
在基类的访问属性 | 继承方式 | 在派生类的访问属性 |
---|---|---|
private | private | 不可访问 |
public | private | private |
protected | private | private |
#include<iostream>
#include<string>
using namespace std;
class Student {
private:
int num;
string name;
char sex;
public:
void get_value(){
cin >> num >> name >> sex;
}
void display(){
cout << num << endl;
cout << name << endl;
cout << sex << endl;
}
};
class Student1:private Student {
private:
int age;
string addr;
public:
void get_value_1(){
cin >> age >> addr;
get_value(); // 在这里访问可以
}
void display_1(){
display();
cout << age << endl;
cout << addr << endl;
}
};
int main(){
Student1 stud;
stud.get_value_1();
// stud.get_value(); // get_value()现在是Student1的私有成员,不能在外界访问
stud.display_1();
return 0;
}
受保护继承
基类的公有成员和受保护成员在派生类中成了受保护成员,基类的私有成员并没有成为派生类的私有成员,仍然时基类的私有成员
保护成员可以被派生类的成员函数引用
如果在一个类中声明了保护成员,就意味着该类可能要用作基类,在他的派生类中访问这些成员
在基类的访问属性 | 继承方式 | 在派生类的访问属性 |
---|---|---|
private | protected | 不可访问 |
public | protected | protected |
protected | protected | protected |
#include<iostream>
#include<string>
using namespace std;
class Student {
protected:
int num;
string name;
char sex;
public:
void get_value(){
cin >> num >> name >> sex;
}
};
class Student1:protected Student { // protected,下一层的派生类可以访问上一层的protected
protected:
int age;
string addr;
public:
void get_value_1(){
cin >> age >> addr;
get_value();
}
};
class Student2: public Student1{
public:
void displayAll(){
cout << num << endl;
cout << name << endl;
cout << sex << endl;
cout << age << endl;
cout << addr << endl;
}
};
int main(){
Student2 stud;
stud.get_value_1();
stud.displayAll();
return 0;
}
派生类成员的访问属性
在建立派生类的时候,并不是简单地把基类的私有成员直接作为派生类的私有成员,把基类的公用成员直接作为派生类的公用成员.
派生类中访问属性 | 在派生类中 | 在派生类外 | 在下层公用派生类 |
---|---|---|---|
public | 可以 | 可以 | 可以 |
protected | 可以 | 不可以 | 可以 |
private | 可以 | 不可以 | 不可以 |
不可访问 | 不可以 | 不可以 | 不可以 |
派生类的构造函数
在声明派生类时,派生类没有把基类的构造函数继承过来,因此,对继承过来的基类成员初始化的工作也要有派生类的构造函数承担.在执行派生类的构造函数时,调用基类的构造函数
派生类构造函数(总参数表):基类构造函数(参数表), 派生类中的成员(参数), ... {}
Student1(int n, string name, char s, int a, string addr):Student(n, name, s), age(a), addr(addr) {}
多重继承
class D:public A, private B, protected C{
派生类新增的成员
}
多重继承的二义性问题
-
两个基类有同名成员
用基类名来限定
c1.A::a=3
-
两个基类和派生类三者都有同名
前面有写,派生类会覆盖基类的同名成员
虚基类
一个派生类有多个直接基类,而这些基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类的数据成员的多分同名成员,但实际上不需要多份拷贝,使用虚基类,使得在继承间接共同基类时只保留一份成员
class A{
A(int i){}
...
};
class B: virtual public A {
B(int n):A(n){}
...
};
class C: virtual public A {
C(int n):A(n){}
...
};
class D: public B, public C {
D(int n):A(n), B(n), C(n){}
...
}
为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类,否则仍然会出现对基类的多次继承
在最后的派生类中不仅要负责对直接基类进行初始化,还要负责对虚基类初始化
多态性
向不同的对象发送同一个消息,不同的对象在接受时会产生不同的行为(即方法).也就是说,每个对象可以用自己的方式去响应共同的消息.所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数.
具有不同功能的函数可以用同一个函数名
通过虚函数实现
#include<iostream>
using namespace std;
class Point {
protected:
float x, y;
public:
Point(float x=0, float y=0):x(x), y(y) {}
friend ostream& operator<<(ostream&, const Point&);
};
class Circle:public Point {
protected:
float radius;
public:
Circle(float x=0, float y=0, float r=0):Point(x,y), radius(r) {}
friend ostream& operator<<(ostream&, const Circle&);
};
class Cylinder:public Circle {
private:
float height;
public:
Cylinder(float x=0, float y=0, float r=0, float h=0):Circle(x,y,r), height(h) {}
friend ostream& operator<<(ostream&, const Cylinder&);
};
ostream& operator<<(ostream& output, const Point& p){
output << "[" << p.x << "," << p.y << "]" << endl;
return output;
}
ostream& operator<<(ostream& output, const Circle& c) {
output << "center=[" << c.x << "," << c.y << "], r=" << c.radius << endl;
return output;
}
ostream& operator<<(ostream& output, const Cylinder& cy) {
output << "center=[" << cy.x << "," << cy.y << "], r=" << cy.radius << ", h=" << cy.height << endl;
return output;
}
int main(){
Cylinder cy1(3.5, 6.4, 5.2, 10);
Point &p = cy1;
Circle &c = cy1;
cout << cy1; // 根据不同的情况调用不同的运算符重载
cout << c;
cout << p;
return 0;
}
center=[3.5,6.4], r=5.2, h=10 center=[3.5,6.4], r=5.2 [3.5,6.4]
纯虚函数
在基类中预留一个函数名,具体功能留给派生类定义
virtual float area() const = 0;
纯虚函数没有函数体
最后面"=0"并不表示函数返回值为0,只是形式上的作用,表示这是纯虚函数
抽象类
不能用来生成对象,定义抽象类的唯一目的就是用它作为基类去建立派生类,为一个类族提供一个公共接口
凡是包含纯虚函数的类都是抽象类.因为纯虚函数不能被调用,所以不能建立对象