内容来自翁恺老师部分视频
基本语法
类的默认构造函数(default constructor)
不带参数的构造函数就是默认构造函数,包括自己定义的和编译器自动给的auto
#include <iostream>
#include <iostream>
using namespace std;
//创建类,在这里声明类的方法
class A {
public:
A(string str); //constructor, similar python class __init__()
~A(); //destructor
};
//构造函数,同类名
//构造器没有返回值(void也不行),名字和类一样,创建对象时自动调用构造函数
A::A(string str){
cout << endl << str << endl;
}
//析构函数(destructor),类名加~
//在对象要被消灭之前时调用,无返回值,无参数
A::~A(){
cout << endl << "----class A will die----" << endl;
}
//在C++中class和struct基本差不多,有细微差别
//在这里相当于定义一个类B,其中声明了B(inta)这个构造器函数
struct B {
float f;
int i;
B(int a);
};
//定义B(inta)构造函数
B::B(int a){
cout << endl <<"struct B init---"<< a << endl;
}
int main()
{
A a("A__init__OK"); //创建对象a,初始化输入string "A__init__OK"
//创建一个对象数组b,每个元素是一个初始化后的B对象,都赋值了构造函数的参数
//B b[2] = {B(1)}; //错误,因为这里只给了第一个元素(对象),告诉编译器再找一个,找不到。。。
//错误类型在两种编译器中是不一样的:
//GUN GCC中:main.cpp|39|error: could not convert '<brace-enclosed initializer list>()' from '<brace-enclosed initializer list>' to 'B'|
//TDM GCC中:39 [Error] no matching function for call to 'B::B()'
B b[2] = {B(1), B(2)}
return 0;
}
this
跟python中的self很像
访问权限
#include <iostream>
//访问限制private friend
using namespace std;
class A{
private:
int i;
int *p;
public:
A(){cout << endl << "A::A()";}
~A(){cout << endl << "A::~A()";}
void haha(int ii){i = ii;} //修改私有变量
void f(A *a){cout << endl << "A::f(A *a) a -> i = " << a->i;} //输出私有变量
};
// friend使用,运算符重载时授权
// 也是在编译时刻检查
struct X;//前向声明
struct Y{
void f(X*);
};
struct X{ //定义
private:
int i; //X的私有成员
public:
void initialize();
friend void g(X*, int);
friend void Y::f(X*);
friend struct Z;
friend void h();
};
void X::initialize(){
i = 0;
}
void g(X* x, int j){
x->i = j;
}
void Y::f(X* x){
x->i = 47;
}
struct Z{
private:
int k;
};
int main()
{
A *p = new A[10]; //开辟一个存放A类型的的数组,并返回指针赋值给p
A a; //创建a对象
a.haha(88); //调用a的haha方法设置a私有属性的值
p[0].f(&a); //p数组第0个元素是一个A对象,调用对象的f方法,传入A类型对象a的指针
//输出88,private相对于类来说的,同类的对象之间可以相互访问私有属性(私有成员变量)
//private的限制只在编译时刻,运行时完全无关,面向对象仅在源代码.cpp中成立
delete[] p;
return 0;
}
class和struct的区别
class A{
int i; //默认private
}
struct A{
int i; //默认public
}
软件重用方式
- 重组(composition):已有对象进行组合,用对象制造对象。玩实的。
包含的方式(内存模型):fully(其他对象就是此其中一部分,其他对象全部在里面)和by reference(只有地址,通过地址访问到其他对象,但不在此内部)
1).所有成员变量都必须在初始化列表中初始化,class Person {;;;}; class Currency {;;;}; class SavingAccount { public: SavingsAccount ( const char* name,const char* address, int cents );//构造函数 ~savingsaccount ();//析构函数 void print(); private: Person m_saver; //完全包含别人的对象 Currency m_balance; //完全包含别人的对象 }; //初始化列表 A::f(): a(0) {} //在构造函数之前就执行a(0) //在初始化列表中调用m_saver、m_balance SavingsAccount::SavingsAccount (const char* name, const char* address, / int cents): m_saver(name, address), m_balance(0, cents) {} void SavingsAccount::print() { m_saver. print(); m_balance. print(); }
2)父类的构造函数调用也必须在初始化列表中初始化。
不放在构造函数里面。否则需要默认构造。 - 继承(inheritance)
在已有类的基础上做改造。玩虚的,类是虚的。
- 父类、基类、超类(Parent、Base、Super)
- 派生类、子类(Derived、Sub、Child)
子类访问父类的private、protected#include <iostream> using namespace std; class A { public: A(): i(0) {cout << "A::A()" << endl;} //成员变量i初始化 ~A() {cout << "A::~A()" << endl;} void print() {cout << "A::f()---" << i << endl;} protected: //protected留给子类访问private的数据 void set(int ii) {i = ii;} private: int i; //成员变量i }; class B: public A { //定义B是A的子类 public: void f() { set(20); //子类可以直接用父类protected的东西,但子类的对象不能直接用,但可以间接访问 print(); //调用父类public函数set()和print() //i = 100; 父类私有的在子类中存在,但不能直接使用,尽管i存在,但不能直接使用。 //error: 'int A::i' is private within this context } }; int main() { B b; //b.set(1); //子类对象直接访问父类protected,出错。子类可以直接用父类protected的东西,但子类的对象不能直接用,但可以间接访问 b.print(); b.f(); //通过f函数间接访问,子类可以直接用父类protected的东西,但子类的对象不能直接用,但可以间接访问 return 0; }
函数隐藏
父类中有几个不同形式的同名函数(overload。要构成overload,两个同名函数的参数表必须不一样),如果子类中出现与父类同名的函数,只保留子类的函数,父类的函数将被隐藏。
默认参数
默认参数不能写在定义处,应该在声明处,在编译阶段自动补上默认值。尽量不要使用默认参数。
#include <iostream>
using namespace std;
void f(int i=10, char c='A');//默认参数不能写在定义处,应该在声明处
int main()
{
f();
return 0;
}
void f(int i, char c) { //默认参数不能写在定义处,应该在声明处
cout << i << endl << c << endl;
}
内联函数(inline)
类型为inline的函数,只是在编译阶段将这段函数的代码插入到调用的地方,再优化,实际是不存在可执行代码里的。以空间换时间,不再需要堆栈处理,效率高。和宏函数有相似功能,但宏没有类型检查,不安全。
- inline函数的body和声明都放在.h文件里面,inline就是声明。递归(需要堆栈)和占用很大空间的函数不能设为内联函数。
xxx.h inline void f(); inline void f() { cout << "inline f()" << endl; }
- 类的成员函数直接写在类声明里面,默认就是inline,不用.cpp去放类的成员函数的body。
xxx.h class A { private: int i; public: void f() {cout << "inline f()" << endl;} }
- 类声明,然后在类外面成员函数前写上inline,也是内联函数。
xxx.h class A { private: int i; public: void f(); } inline void f() {cout << "inline f()" << endl;}
const
被const的对象一定要初始化
- const是变量
extern const int bufsize; //声明外部的全局变量bufsize在这里是const,而bufsize本身不一定是const类型
//做数组大小正确使用
const int a_size = 12;//在编译阶段就完成了size的初始化,
int a[a_size]; //size的值已知,所有这里合法
//错误使用
extern const int b_size;//只告诉编译器外部有这个东西,具体值多少不知道
int b[b_size]; //错误
- const与指针
//const与*谁前谁后
char * const q = "abc"; //q是const,q不能被修改
*q = 'd'; //ok,但是q所指的空间是个普通字符变量
q++; //error,q是const
const char *p = "abc"; //*p是const,通过p不能对p所指内容进行修改
*p = 'c'; //error
p++; //ok
Person p1("Fred", 200);
const Person* p = &p1; //指针是const
Person const* p = &p1; //指针是const
Person *const p = &p1; //对象是const
const Person* const p = &p1; //指针和对象都是const
本地变量在堆栈里面,全局变量在全局数据区,常量在代码段里,代码段不可写。
char *a = "hello world!";//定义了一个指针a,a指向代码段const
char s[] = "hello world!";//定义一个数组s,系统从代码区拷贝const到堆栈区
a[0] = 'a'; //a指向的空间为const空间,在代码段里,可读不可写
3.非const变量当成const变量使用(non-const —> const)
void f(const int* x);
void f(const int* x){ //形参为const类型,函数内部保证不会更改x的对象
(*x)++; //形参x的对象为const类型,此处会报错error: increment of read-only location '* x'|
cout << *x << endl;
}
int b = 10;
f(&b);
4.成员函数const
class A {
private:
int i;
public:
A():i(0){cout << "A::A()" <<endl;}; //在构造函数中初始化成员变量i
void ff() const; //声明和定义处都const
};
void A::ff() const { //声明和定义处都const
i++; //error: increment of member 'A::i' in read-only object
// 不能改变成员变量,相当于this是const
cout << i << endl;
}
- const放在成员函数后面和无const成员函数构成overload关系
class B {
private:
int i;
public:
B():i(0) {}
void f() {cout << "f()" << endl;}
void f() const {cout << "f const" << endl;}//与上构成overload关系
};
const B b; //由B定义一个const类型的对象b
b.f();//输出f const 因为this为const类型
B bb;
bb.f();//输出f() 因为this为普通类型,调用f()
构成overload关系的关键是两个函数参数表不同。
两个f函数的参数表分别为:
void f(A* this)
void f(const A* this)
references/alias(引用/别名)
引用 定义时必须必须初始化,引用作为形参时,在函数内部可以直接改变被引用的变量(C只通过指针改变)。本身就是用* const p实现,一经定义,无法改变。引用不是实体。
char c;
char &r = c;//引用初始化为c,与c绑定
int a;
const int& z = a;//z是x的别名(引用),但是不能通过z修改a,跟前面指针一样
int i;
int&j = i;
j = 10;//i跟着变为10
func(int& );
int k=2;
func(k * 3);//报错,func的参数是一个引用,这里k*3不是应用,仅仅是个临时的结果
int&* p;//错误,p定义为指针,
int*& p;//ok,p是引用,p捆绑的变量是个int型指针变量
upcasting(向上造型)
把子类当做父类对象看待。向上造型是将派生引用或指针转换为基类引用或指针的操作。向下造型有风险。
Employee父类,Manager子类