面向对象程序设计¶
约 1241 个字 147 行代码 2 张图片 预计阅读时间 5 分钟
任课教师:许威威
Lec1¶
c(onstruc)tor: 构造函数,函数名和类名相同
Lec2¶
- 全局变量(Global Variable):定义在函数外部的变量,使用
extern
关键字可以使用其他文件中定义的全局变量- 静态全局变量(Static Global Variable):只能在当前文件中使用
- 局部变量(Local Variable):定义在函数内部的变量,只能在函数内部使用
- 静态局部变量(Static Local Variable):其值在函数调用结束后不会改变,下次调用时仍然可以使用
Pointer¶
创造了一个 string
类型的对象,调用了构造函数
创造了一个指向 string
类型的指针,指向的 string
对象还没有被创建
动态分配内存¶
C++ uses new
and delete
返回申请内存的首地址(因此一般赋值给一个指针)(?)。和 malloc
不同的是,new
是创建了一个对象的实例,因此调用了其构造函数(delete
时也调用析构函数)
a = new int [10]; delete [] a;
释放 10 个 int
类型的内存,delete a
只释放一个 int
类型的内存
不同于 free(NULL)
,delete
一个空指针是安全的
Reference¶
Ref 定义时必须赋初值,且引用的对象不再改变(即后面如果出现给 r
赋值的操作,实际上是给 c
赋值)
- 不能 Ref Ref
- 不能存在指向 Ref 的指针(
char& *p // illegal
)- 可以有引用指针的 Ref(
char* &p // ok
)
- 可以有引用指针的 Ref(
- 没有 Ref 数组
const¶
不允许修改
- Compile-time const:
const int a = 10;
,在编译时就确定了(是 symbol 的 entry) - Run-time const:
cin >> x; const int a = x;
,在运行时才确定,例如接受用户输入
Aggregates
const int a[] = {1, 2, 3}; float f(a[1]);
会报错,因为编译器在编译时不会深究 const 数组内部的东西
Pointers
char * const q = "hello";
指针 q 是 const,于是q++
会报错,但是*q = 'x'
不会char const* q
大概率同理(可能因为不同编译器解释不同)
const char* p = "hello";
*p
是一个 const char,于是*p = 'x'
会报错
struct S {
void f() { std::cout << "non-const\n"; }
void f() const { std::cout << "const\n"; }
};
int main() {
S a;
const S b;
a.f(); // non-const
b.f(); // const
const S *ps = &b;
ps->f(); // const
}
// <https://tree.45gfg9.net/lcppthw/construct/#const>
const
出现在函数参数列表后说明这个函数影响 this
的类型,且不会改变任何成员变量
Stash¶
- Typeless container
- 存储相同类型的对象
- 使用类型的大小初始化
Deconstructor¶
当对象 Go out of scope 时,调用析构函数
Initialization vs. assignment
Student::Student(string n) { name = n;}
: Assignment(Inside Constructor)Student::Student(string n) : name(n) {}
: Initialization(Before Constructor)
Lec3¶
Static¶
已知:当 static 修饰全局变量时,限制该变量的作用域为当前文件;当 static 修饰局部变量时,表示该变量的生命周期为整个程序运行期间。
static 还可以修饰成员变量,静态成员变量/函数由整个 Class 共享,非静态成员变量/函数由每个对象拥有,且静态成员变量必须在类外部被定义一次(类内部的 static <type> <name>;
只是声明)
struct rectangle {
static int count;
double width;
double height;
rectangle(double w, double h) : width {w}, height {h} {
count++;
}
~rectangle() {
this->count--;
}
};
int rectangle::count = 0; // must!
int main() {
rectangle c1 {3, 4};
std::cout << rectangle::count << "\n"; // 1
rectangle c2 {5, 6};
std::cout << c2.count << "\n"; // 2
}
// <https://tree.45gfg9.net/lcppthw/construct/#static>
拷贝构造函数¶
声明长这样:T::T(const T& t);
C++ 会提供一个默认的拷贝构造函数,拷贝对象的每个成员变量(memberwise copy),但是如果成员变量是指针类型,则只会拷贝指针的值(即地址),而不是指针所指向的内容 – Copies each member variable - Good for numbers, objects, arrays – Copies each pointer - Data may become shared!
拷贝函数何时被调用?
- 在对象被 call by value 时,例如被传入函数参数时
- 在被赋值为另一个对象时
T t2; T t1 = t2;
- 在返回对象时,例如
T rtsomething(){T t1("1"); return t1;} T whoami = rtsomething();
namespace¶
命名空间就是“更高级层面的”隔离,包含逻辑上的一组类、函数、变量
用 using namespace \<name> 可以引入命名空间中的所有内容,但是可能导致命名冲突
可以用 nameplace short = WhatALongNameplaceName;
来 alias 一个很长的命名空间
Nameplace Composition
用于合并命名空间,还可以解决命名冲突。
可以在多个文件里给同一个命名空间添加内容
抽象基类¶
抽象基类是一个包含纯虚函数的类,不能被实例化。纯虚函数是没有实现(body)只有 interface 的虚函数,必须在派生类中实现。
运算符重载¶
+ - * / % ^ & | ~ = < > += -= *= /= %= ^= &= |= << >> >>= <<= == != <= >= ! && || ++ -- , ->* -> () []
可以重载
. .* :: ?: sizeof typeid static_cast dynamic_cast const_cast reinterpret_cast
不行
class Integer {
public:
Integer( int n = 0 ) : i(n) {}
const Integer operator+(const Integer& n) const{
return Integer(i + n.i);
}
...
private:
int i;
};
Integer x(1), y(5), z;
// z = x + y //ok
// z = x + 3 //ok
// z = 3 + x // illegal
const Integer& Integer::operator++() {
*this += 1; // increment
return *this; // fetch
}
// int argument not used so leave unnamed so
// won't get compiler warnings
const Integer Integer::operator++( int ){
Integer old( *this ); // fetch
++(*this); // increment
return old; // return
}
recall const
全局写的有关一个类的运算符重载函数应当被设置为这个类的 friend,因为其要访问类的所有属性
类型转换 ops 的重载
Template¶
模板函数¶
模板类¶
这样定义之后,注意 Rmqc
并不能算是一个类(只是一个模板),而 Rmqc<int>
才是一个(被实例化的)类
可以看出来模板是更高级别的一层抽象,类是模板的实例化,对象又是类的实例
这就解释了在继承时:
// 非模板类继承于模板类,需要实例化模板(确定typename)
template <typename T>
class Base {
...
}
class Derived : public Base<int> {
...
}
// 模板类继承于非模板类,直接继承
class Base {
...
}
template <typename T>
class Derived : public Base {
}
// 模板类继承于模板类,不用实例化模板,但要注明 typename T
template <typename T>
class Base {
...
}
template <typename T>
class Derived : public Base<T> {
...
}