面向对象编程(一)基础知识

类不是实体,对象是实体

成员变量(filed)属于对象

成员函数(member function)属于类

初始化列表

列表初始化(initialize list)仅对成员变量初始化。

在构造函数里对成员变量初始化则为先初始化(默认)后赋值,故所有成员变量必须要有默认的初始化方法(成员变量包含其他类但该类没有默认构造函数则会报错)。构造函数无法主动调用。

尽量使用列表初始化

1
2
3
4
5
6
class A{
public:
int a;
A(int i):a(i){}; //initialize list
// A(int i):{a=i;}
};

拷贝构造和拷贝赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Test
{
public:
Test() : ptr(0)
{
ptr = new char[1];
ptr[0] = '\0';
cout << "call Test()" << endl;
};

Test(const char *c_ptr)
{
if (c_ptr)
{
ptr = new char[strlen(c_ptr) + 1];
}
else
{
ptr = new char[1];
ptr[0] = '\0';
}
cout << "call Test(const char *c_ptr)" << endl;
};

Test(Test &test)
{
ptr = new char[strlen(test.ptr) + 1];
strcpy(ptr, test.ptr);
cout << "call Test(Test &test)" << endl;
};

Test &operator=(Test &test)
{
if (this == &test) //1.prevent meaningless copy 2.because will delete later, prevent error test.ptr
return *this;
delete[] ptr;
ptr = new char[strlen(test.ptr) + 1];
strcpy(ptr, test.ptr);
cout << "call copy operator=" << endl;
return *this;
}

private:
char *ptr;
};

int main()
{
Test test; //initialzation,call Test()
Test test1("123"); //initialzation,call Test(const char *c_ptr)
Test test2 = test1; //initialzation,call Test(Test &test)
Test test3(test1); //initialzation,call Test(Test &test)
test3 = test2; //assignment,call copy operator=
}

拷贝构造

通过类的其他对象初始化当前对象(有默认拷贝构造方法,成员对成员的拷贝,可能发生对象的嵌套拷贝)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//During call by value
void roster(Person){};
Person child("Ruby");
roster(child); // call copy ctors

// During initialization, call copy ctor
Person baby_a("Fred");
Person baby_b = baby_a;
Person baby_c(baby_a); // same as above

//During function return
Person captain(){
Person player("George");
return player;
}

T::T(const T&)

建议每个类提供默认构造函数,拷贝构造函数,虚函数

继承

访问属性

访问限制符\访问位置 当前类 子类 类外
public 可以 可以 可以
protected 可以 可以 不可以
private 可以 不可以 不可以

若子类中含有与父类同名函数(函数名相同,参数列表个数和类型),父类的函数会被隐藏。(仅C++如此)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class A{
public:
int i;
A(int a):i(a){};
void print(){cout<<"A::print()"<<endl;};
void print(int a){cout<<"A::print(int i)"<<endl;};
};

class B:public A{
public:
B(int a):A(a){};
void print(){cout<<"B::print()"<<endl;};
};

int main(){
B b(4);
b.print(2); //ERROR, cant found print(int a) in A
}

友元

类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数称为它的友元

1
2
3
4
5
6
7
class Sales_data{
friend Sales_data add(const Sales_data&, const Sales_data&);
public:
//...
private:
//...
};

一般来说,最好在类定义开始货结束前的集中位置声明友元

友元的声明只指定了访问的权限,而非一个通常意义上的函数声明。如果我们希望类的用户能够调用某个友元函数,那么就必须在友元声明外再专门对函数进行一次声明

相同类的对象互为友元

虚函数

向上造型(upcasting)指将派生类的引用或指针转化为父类的引用或指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Base
{
public:
Base() : i(0) {}
void print()
{
cout << "Base print" << endl;
}

private:
int i;
};

class Derived : public Base
{
public:
void print()
{
cout << "Derived print" << endl;
}
};

void test_print(Base &a)
{
a.print();
}


int main()
{
Base test;
Derived test2;
test_print(test2); //Base print
return 0;
}

虚函数:使派生类各自定义适合自己版本的函数,当使用基类的引用(或指针)调用一个虚函数时将发生动态绑定(父类加上virtual即生效,但最好子父类都加上virtual)

静态绑定:编译时确定调用的函数

动态绑定:运行时确定调用的函数

如果派生类没有覆盖基类的虚函数,则派生类会直接继承基类中的版本

对象增加vptr,指向当前对象的类的vtable(属于类,保存存储虚函数的地址)

虚函数运行方式为访问vptr—访问vtable—访问函数指针—访问函数

如果有一个虚函数,则需将析构函数设为虚函数,以处理新增变量

C++默认静态绑定

派生类虚函数的形参类型个数,返回值类型必须和基类一致,否则为重载(除非返回类型时类本身的指针或引用)

其他

private针对类而非对象,可访问另一对象中的private成员

作者

Martin

发布于

2023-05-26

更新于

2023-05-26

许可协议

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

评论

You forgot to set the shortname for Disqus. Please set it in _config.yml.

You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.