某学校专业课OOP复习
因为只是为了让自己看懂,所以写的比较简略,就这样
定义 分配内存单元
声明 不涉及内存分配
访问 读取修改内存所存放的数据
左值(Lvalue)
右值(Rvalue)
R->Readable可读取
后面一起讲罢
数据类型 & 引用名 = 已存在的变量名
可以对字面常量、符号常量、甚至变量取别名
const 数据类型 & 引用名 = 常量;
const 数据类型 & 引用名 = 已存在的变量名;
可以将变量视为常量,但不能将常量作为变量。
无形式参数
传值型
引用型
void
值返回
引用返回
类的成员默认是private
例如:
class Time
{
private: // 私有的(访问属性)
int h, m, s;
};
Time t;
cout << sizeof(t) << endl;
// 输出12,这里sizeof(int)为4
//这里的12个字节,存放着对象t的h, m, s的值。
Time t1(10, 20, 30);
//则 t1 占12个字节。h, m, s的值分别为10, 20, 30
对象的非静态数据成员所占用的空间总和为对象的基本空间
至于静态怎么样我们下期再说
一个对象的基本空间占sizeof(类型名或对象名)字节;
注意到:
- 运算符sizeof能够测量对象的基本空间及对象的资源空间所占用的内存单元字节数的总和。 (×)
资源空间可不能测量(因为基本空间里面只有指针?)
Time::Time(Time *const this, int hour,int minute, int second)
非静态成员函数中会有一个隐含的this指针常量。
因此参数表的第一个永远都是this
同时,也会在非常量成员函数中传入this
int main()
{
Time t1, t2(10, 20, 30), *p = &t1;
t1.Show( &t1 ); // 相当于传递对象t1的地址
t1.Settime( &t1, 8, 30, 0);
p->Show( p ); // 相当于传递指针变量
t2.Show( &t2 );
return 0;
}
不能修改成员的属性
格式:在所声明及定义的成员函数首部后添加保留字const
实际上是将this变为常量指针常量,故无法对本对象进行修改。
int Time::GetHour() const // 定义常量成员函数
{
return h;
}
int Time::GetHour(const Time *const this)
const // 常量成员函数中的隐含参数this为常量指针常量
{
return h;
}
值得说明的是,const int *p代表指向内容不可变,int *const p则代表指针本身不可变
常量成员函数的主要作用是处理常量对象
程序中直接定义常量对象并不多见,最常见的是出现在函数形式参数位置上,对实参目标的保护:
- 常量引用(形参)所绑定的实参对象
- 常量指针(形参)所指向的目标对象
例如
void Print(const Time &t, const Time *p)
{
t.Show();
p->Show();
}
若Time::Show不是常量成员函数,则编译不通过。
编译器会认为:
联系前文,会引用传入隐含参数this,违反了不能将常量当作变量
class Test
{
public:
Test():a(0){ }
private:
const int a; // 常量数据成员
};
类中常量数据成员必须在构造函数的初始化列表中进行初始化!!!!
因为一旦进入构造函数,此常量数据成员不能再改变。
分离文件
用泛型写的模板称为类模板
类模板实例化后则是模板类
值得注意的是:类模板得写在头文件中
template <typename T> class Vec2
{
public:
// 其他成员函数(略)
private:
T a, b; // 数据成员的数据类型待定
};
构造函数用于创建对象,析构函数则用于销毁对象
赋值并没有创建对象,请注意和初始化区分
非常有趣的一点是,在初始化对象,如
Time t=10
中,这里的等于号“=”不是赋值,而是初始化记号,当然这是为了继承C语言的风格而做出的妥协,C++风格的写法应该是Time t(10)
任何类都至少有两个构造函数(构造与拷贝构造)
不需要实参的构造函数
未定义构造函数时,系统会提供一个默认构造函数
该函数将不对对象的数据成员进行初始化。所创建的对象数据成员的初值取决于对象的存储类型。
若定义了构造函数,系统将不再提供默认的构造函数。
仍需要,可以重载,通用的方法是
设计所有参数均带默认值的构造函数。当不提供实参时,该构造函数便成为默认的构造函数。
创建对象时不显式地初始化
创建对象、数组
Time t, ta[10];
Complex z, za[10];
创建堆对象、数组
Time *pt0, *pt1; // 仅定义指针变量
Complex *pz0, *pz1; // 没有创建对象
pt0 = new Time; // 创建1个对象
pt1 = new Time[10]; // 创建10个对象
pz0 = new Complex; // 创建1个对象
pz1 = new Complex[10]; // 创建10个对象
需要注意的当然是对象得new出来才能算是创建
同时注意,语句:
Time t();
Complex z();
均不是创建对象,而是函数原型,用于声明无参数的函数。
带一个实参的构造函数被称为转换构造函数。
和强制转换运算符区分
之所以称这种构造函数为转换构造函数,是因为可以用它实现数据类型的自动转换或强制转换。
引用本类对象(为什么是引用?)
场合(如创建副本时)
浅拷贝和深拷贝
? 独立空间
冒号语法,函数体内为赋值运算
声明顺序,与冒号语法无关
静态本质上是普通成员/函数加上了访问域控制
静态成员关键字一样,和其他“静态”无联系
类外初始化,如: int Student::num = 0;
生命周期
修改?
只认类型不认对象
不含this指针
直接 类名::静态成员函数名(实参)
因为可能不创建对象
例子:设置导师
不默认联系对象(this),显示传递
朋友的朋友不是我的朋友
友元和成员函数
操作数不同
值返回
引用返回
友元
第一个操作数是i/ostream对象
引用返回对象->使连续操作成为可能
成员函数不必传参
例如++a
或--a
实现
Date & Date::operator++() // 前增量运算符函数
{
days[1] = isLeapyear() ? 29 : 28;
day++;
if(day > days[month-1]){ day = 1; month++; }
if(month>12){ month = 1; year++;}
return *this; // 引用返回“本对象”,可作左值
}
例如,对象++
和 对象--
// 后增(减)量运算。难点:
// 运算符函数需要实现“既返回对象的原值,又修改对象的值”
Date Date::operator++(int) // 后增量运算符函数
{
Date temp(*this); // 拷贝构造对象,保存本对象的原值
++(*this); // 利用前增量运算符函数
return temp; // 返回局部自动对象,不能引用返回
}
这里我们可以看到,利用先前的前自增是一种高效的方式
[]运算符
资源释放?
由基类派生其他类的语法格式:
class 派生类名 : public 基类类名
{ // 新增加的属性和行为
// 或者基类成员函数的覆盖、重载
}
构造顺序
析构则相反
基类及其子类共用一个静态数据成员
向上兼容,派生类可以初始化基类对象,但基类对象不能初始化派生类对象
先期联编
迟后联编
基类指针操作派生类成员,若不使用虚析构函数,则可能会导致其无法调用派生类析构函数
或者这么想——虚函数更类似于一种标记,要求程序匹配具体的对象进行操作
不过,值得注意的是:
void Office1(Student x, double credit)
{// 形参为值传递,创建学生对象,不办实事
x.SetCredit(credit);
x.Tuition();
cout << x << endl;
}
像这种情况,值传递创建的是基类对象,不享受多态性
纯虚函数,函数声明后有=0
原文:https://www.cnblogs.com/shudorcl/p/14043927.html