0、多态 封装 继承
多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果
多态的三个条件:
- 继承的存在
- 子类重写父类的方法
- 父类引用变量指向子类对象
封装:封装的意义在于保护或者防止代码(数据)被我们无意中破坏。
继承:继承主要实现重用代码,节省开发时间
// 面向对象的五大基本原则
单一职责原则(SRP)
开放封闭原则(OCP)
里氏替换原则(LSP)
依赖倒置原则(DIP)
接口隔离原则(ISP)
一、返回值
C中:如果函数未指定返回值类型,则默认为int
c++中:如果一个函数没有返回值,返回值类型必须指定为void
二、参数列表
C中:如果函数没有指定参数列表,则默认可以接受任意多个参数
C++中:有严格的类型检测,没有参数列表的函数默认为void,不接受任意参数
三、缺省参数(即给参数一个默认值)
C:不支持
C++:支持(如果没有指定实参则使用缺省值,有则使用指定实参)
1.默认实参必须在参数列表的结尾
2.默认参数只能出现在函数声明或者定义二选一中
3.缺省值必须是常量或全局变量
4.缺省参数必须是值传递或者常参传递
四、函数重载
C:不支持
C++:支持在同一作用域中存在几个功能类似的同名函数,但参数列表(参数个数、类型、顺序)不同
五、引用和指针
引用:可以看做是一个变量的别名
特点:1.必须初始化
2.一个变量可以有多个引用
3.引用一旦初始化,就不能在成为其他变量的引用
ps:数组不能被引用
引用与指针的异同:
同:底层实现相同
异:1.引用必须初始化
2.引用一旦绑定就不能更改
3.++的结果不同
4.有多级指针,没有多级引用
多态 封装 继承
2、const和const_cast强制转换
符号表 只读
// 字面量初始化的const常量进入符号表
// 其他变量初始化的const常量只读变量
int main(void)
{
const int a = 7;// 每次直接从符号表读取,而不是内存读取,但是也分配内存
// 加入volatile修饰时,编译器不做优化,从内存地址读取数据
int *b = (int *)&a;
*b = 100;
printf("%d\n", a); // 7 进入符号表,值不会变
}
// volatile修饰的const常量不会进入符号表
// 在extern和&修饰的const常量时,会分配空间,但是不会去使用
int main()
{
const int x = 1;// 进入符号表
const int& rx = x;// 常量引用
int& nrx = const_cast<int&>(rx);// 去除只读属性
nrx = 5;
printf("x = %d\n", x); // 1
printf("rx = %d\n", rx); // 5
printf("nrx = %d\n", nrx); // 5
printf("x = %p\n", &x); // 0xaabbccdd
printf("x = %p\n", &rx); // 0xaabbccdd
printf("x = %p\n", &nrx); // 0xaabbccdd
/////////////////////////////////////////////
volatile const int y = 2; // 没进入符号表
int* py = const_cast<int*>(&y);
*py = 5;
printf("y = %d\n", y); // 5
printf("py = %p\n", &py); // 0xasdfghj
////////////////////////////////////////////
const int z = y; // y 是不确定的变量,所以z是只读变量
py = const_cast<int*>(&z); // 去除只读属性
*py = 5;
printf("y = %d\n", z); // 5
printf("py = %p\n", &py); // 0xasdfghj
/////////////////////////////////////////////
char c = 'c';
char& rc = c;
const int& trc = c; // 不同类型间的常量引用分配新的空间
rc = 'a';
printf("c = %c\n", c); // a
printf("rc = %c\n", rc); // a
printf("trc = %c\n", trc); // c
printf("c = %p\n", &c); // 0xaabbccdd
printf("rc = %p\n", &rc); // 0xaabbccdd
printf("trc = %p\n", &trc); // 0xaabbdcda
return 0;
}
// 区别于C++,C语言中const修饰的变量本质上还是变量,其值只有在运行时才能确定
// #define 被预编译处理,没有作用域的概念
// const有作用域和类型检查
void f()
{
#define a 3// 宏使被预处理的 直接文本替换
const int b = 4;// const作用域只在此函数中
}
void g()
{
printf(“a = %d\n”, a);//
//printf(“b = %d\n”, b);// error
}
int main()
{
const int A = 1;// 常量被编译进符号表
const int B = 2;
int array[A + B] = {0};
int i = 0;
for(i=0; i<(A + B); i++)
{
printf("array[%d] = %d\n", i, array[i]);
}
f();
g();
return 0;
}
// const修饰的对象为只读对象,只能调用const的成员函数,其成员变量不能直接被修改
3、三目运算
// C++三目运算可以返回a或b的引用,可以当左值。但是其中有常量时,不能当左值
int a = 1;
int b = 2;
( a<b ? a : b) = 3;// ok
( a<b ? 1 : b) = 3;// error
4、引用和const引用(常量引用)
// 引用在初始化的时候必须进行初始化,类型要一致
int main(int argc, char *argv[])
{
int a = 4;
int& b = a; // b是a的别名
b = 5;
printf("a = %d\n", a); // 5
printf("b = %d\n", b); // 5
printf("&a = %p\n", &a); // 0xaabbccdd
printf("&b = %p\n", &b); // 0xaabbccdd
return 0;
}
// const引用可以用常量初始化,但非const引用不能用常量初始化。
const int &ir = 10;
int &jr = 10; // error
// 只有const引用才能被临时变量或临时对象初始化
// 另一个由于对象的调用会自动调用构造函数,对象的引用可以提高效率
void print(string& str)
{
cout<<str<<endl;
}
print(“hello world”); // 如此调用会报错误
出错的原因是编译器根据字符串”hello world”构造一个string类型的临时对象,这个临时对象具有const属性。当这个临时对象传递给非const的string&引用类型时,因为非const引用绑定对象时,要求该对象也是非const对象。而在这时,因为string类型的临时对象是const对象,所以就出现错误。因此,解决办法就是将print()函数的参数改为常引用。代码修改如下,可顺利通过编译。
void print(const string& str)
{
cout<<str<<endl;
}
print(“hello world”);// 顺利通过编译
// const引用不能被用于修改其绑定的对象,其值拥有只读属性
void Example()
{
int a = 4;
const int& b = a;// b拥有只读属性
int* p = (int*)&b;
//b = 5; // error
*p = 5;
printf("a = %d\n", a); // 5
printf("b = %d\n", b); // 5
}
void Demo()
{
const int& c = 1;
int* p = (int*)&c;
//c = 5; // error
*p = 5;
printf("c = %d\n", c); // 5
}
int main(int argc, char *argv[])
{
Example();
Demo();
return 0;
}
// 引用的实现是一个指针常量
struct TRef
{
char& r;
};
int main(int argc, char *argv[])
{
char c = ‘c’;
char& rc = c;//rc c
TRef ref = { c }; // rc
printf("sizeof(char&) = %d\n", sizeof(char&)); // 1
printf("sizeof(rc) = %d\n", sizeof(rc)); // 1
printf("sizeof(TRef) = %d\n", sizeof(TRef)); // 4 本质是指针
printf("sizeof(ref.r) = %d\n", sizeof(ref.r)); // 1
return 0;
}
// 引用不能够完全避免内存操作的失误
int& demo()
{
int d = 0;
printf("demo: d = %d\n", d);
return d; // 不能返回局部变量的引用
}
int& func()
{
static int s = 0;// 拥有全局生命周期,但制作用于局部变量
printf("func: s = %d\n", s);
return s;
}
int main(int argc, char* argv[])
{
int& rd = demo();// 0
int& rs = func();// 0
printf("\n");
printf("main: rd = %d\n", rd); // 11223344 代表的是一个不存在的变量
printf("main: rs = %d\n", rs); // 0
printf("\n");
rd = 10;
rs = 11;
demo(); // 0
func(); // 11
printf("\n");
printf("main: rd = %d\n", rd); // 11223344 已经不存在
printf("main: rs = %d\n", rs); // 11
printf("\n");
return 0;
}
4、内联函数
// 内联函数省去函数调用时的压栈,跳转,和返回的开销,编译器会把该函数的代码副本放置在每个调用函数的地方
// inline只是一种请求,编译器你可以拒绝
•1.在内联函数内不允许使用循环语句和开关语句;
•2.内联函数的定义必须出现在内联函数第一次调用之前;
•3.类结构中所在的类说明内部定义的函数是内联函数。
#define FUNC(a, b) ((a) < (b) ? (a) : (b))
inline int func(int a, int b)
{
return a < b ? a : b;
}
int main(int argc, char *argv[])
{
int a = 1;
int b = 3;
int c = FUNC(++a, b);
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);
return 0;
}
// VC的强制内敛 __forceinline
// g++的强制内敛 attribute((always_inline))
// C++的强制内敛 inline
//__forceinline
//attribute((always_inline))
//inline
int add_inline(int n);
int main(int argc, char *argv[])
{
int r = add_inline(10);
printf(" r = %d\n", r);
return 0;
}
inline int add_inline(int n)
{
int ret = 0;
for(int i=0; i<n; i++)
{
ret += i;
}
return ret;
}
5、函数参数的扩展
// 函数参数可以提供默认值
// 默认参数可以在定义时或者声明时指定,提供的参数在被调用时要被看见。
int mul(int x = 0);
int main(int argc, char *argv[])
{
printf("%d\n", mul());// 0
printf("%d\n", mul(-1));// 1
printf("%d\n", mul(2));// 4
return 0;
}
int mul(int x)
{
return x * x;
}
// 参数的默认值从右向左提供
// 参数的调用从左向右
int add(int x, int y = 0, int z = 0);
int main(int argc, char *argv[])
{
printf("%d\n", add(1)); // 1
printf("%d\n", add(1, 2));// 3
printf("%d\n", add(1, 2, 3));// 6
printf("%d\n", add(1, , ));// 6
printf("%d\n", add( , 2, 3));// error x默认 y z没有默认
return 0;
}
int add(int x, int y, int z)
{
return x + y + z;
}
//为了兼容C语言,定义时可以用占位参数,一般不使用占位参数
int func(int x, int = 0);
int main(int argc, char *argv[])
{
printf("%d\n", func(1));
printf("%d\n", func(2, 3));
return 0;
}
int func(int x, int)// 占位
{
return x;
}
6、重载
// 用同一个函数名定义不同的函数
// 参数个数不同 参数类型不同 参数顺序不同 之一
#include <stdio.h>
#include <string.h>
int func(int x)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
int main(int argc, char *argv[])
{
printf("%d\n", func(3));
printf("%d\n", func(4, 5));
printf("%d\n", func(“D.T.Software”));
return 0;
}
// 函数默认参数和函数重组
int func(int a, int b, int c = 0)
{
return a * b * c;
}
int func(int a, int b)
{
return a + b;
}
int main(int argc, char *argv[])
{
int c = func(1, 2); // error
return 0;
}
// 函数重载本质是相互独立的不同函数
int add(int a, int b) // int(int, int)
{
return a + b;
}
int add(int a, int b, int c) // int(int, int, int)
{
return a + b + c;
}
int main()
{
printf("%p\n", (int()(int, int))add);
printf("%p\n", (int()(int, int, int))add);
return 0;
}
// 函数重载和函数指针
int func(int x)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
typedef int(*PFUNC)(int a);// 函数指针
int main(int argc, char *argv[])
{
int c = 0;
PFUNC p = func;
c = p(1);
printf("c = %d\n", c);
return 0;
}
///////////////////////////////
#ifdef __cplusplus
extern “C” {
#endif
//////////////
#include “add.h”
//////////////
#ifdef __cplusplus
}
#endif
int main()
{
int c = add(1, 2);
printf("c = %d\n", c);
return 0;
}
7、动态内存分配 命名空间
// 分配空间时可以初始化
// int* p = new int(); delete p;
// p = new int[10]; delete[] p;
int main()
{
int* p = new int;
*p = 5;
*p = *p + 10;
printf("p = %p\n", p);
printf("*p = %d\n", *p);
delete p;
p = new int[10];
for(int i=0; i<10; i++)
{
p[i] = i + 1;
printf("p[%d] = %d\n", i, p[i]);
}
delete[] p;
return 0;
}
// 命名空间解决全局变量冲突的问题
namespace First
{
int i = 0;
}
namespace Second
{
int i = 1;
namespace Internal
{
struct P
{
int x;
int y;
};
}
}
int main()
{
using namespace First;
using Second::Internal:😛;
printf("First::i = %d\n", i);
printf("Second::i = %d\n", Second::i);
P p = {2, 3};
printf("p.x = %d\n", p.x);
printf("p.y = %d\n", p.y);
return 0;
}
8、强制类型转换
static_cast(expression)
// static_cast:用于基本类型转换,不能用于基本类型的指针转换,用于继承关系对象的转换和类指针的转换
// const_cast:去除变量的只读属性,用于指针或引用
// dynamic_cast :用于继承关系的类指针转换,用于有交叉关系的类指针的转换,具
有类型检查功能,需要虚函数支持
// reinterpret_cast:用于指针间转换,用于整数和指针见的转换
void static_cast_demo()
{
int i = 0x12345;
char c = ‘c’;
int* pi = &i;
char* pc = &c;
c = static_cast<char>(i); // 基本类型转换
pc = static_cast<char*>(pi); // 基本指针类型转换
}
void const_cast_demo()
{
const int& j = 1;// 只读变量
int& k = const_cast<int&>(j);// 去除只读属性
const int x = 2; // 进入了符号表
int& y = const_cast<int&>(x); //没去除只读属性,重新分配去除只读属性的变量空间
int z = const_cast<int>(x); // error 只用于引用和指针
k = 5;
printf("k = %d\n", k); // 5
printf("j = %d\n", j); // 5
y = 8;
printf("x = %d\n", x); // 2
printf("y = %d\n", y); // 8
printf("&x = %p\n", &x); // 0xaabbccdd
printf("&y = %p\n", &y); // 0xaabbccdd
}
void reinterpret_cast_demo()
{
int i = 0;
char c = ‘c’;
int* pi = &i;
char* pc = &c;
pc = reinterpret_cast<char*>(pi); // 指针间的转换
pi = reinterpret_cast<int*>(pc);
pi = reinterpret_cast<int*>(i); // 指针和整形间的转换
c = reinterpret_cast<char>(i); // error
}
void dynamic_cast_demo()
{
int i = 0;
int* pi = &i;
char* pc = dynamic_cast<char*>(pi);// error 需要虚函数支持的类函数之间的转换
}
int main()
{
static_cast_demo();
const_cast_demo();
reinterpret_cast_demo();
dynamic_cast_demo();
return 0;
}
9、类与封装
// 类成员的访问
int i = 1;
struct Test
{
private:
int i;
public:
int j;
int getI()
{
i = 3;
return i;
}
};
int main()
{
int i = 2;
Test test;
test.j = 4;
printf("i = %d\n", i); // 2
printf("::i = %d\n", ::i); // 1 全局作用域
printf("test.i = %d\n", test.i); // Error 私有变量
printf("test.j = %d\n", test.j); // 4
printf("test.getI() = %d\n", test.getI()); // test.getI() = 3
return 0;
}
11、类的成员 - 构造函数
// 构造函数的作用是对对象自动初始化
// 构造函数与类名相同,没有返回值,在对象定义时自动被调用
// 栈和堆上创建对象时,成员变量初始值为随机值
// 静态储存区和全局区上创建对象时,成员变量初始值为0
// 不能为虚函数,虚函数是为了对象的动态调用,对象都没构造,哪来的虚函数。
class Test
{
private:
int i;
int j;
public:
int getI() { return i; }
int getJ() { return j; }
Test()// 构造函数
{
i = 1;
j = 1;
}
};
Test gt;// 全局区对象
int main()
{
printf(“gt.i = %d\n”, gt.getI());// 全局数据区统一为0
printf(“gt.j = %d\n”, gt.getJ());
Test t1; // 栈区对象
printf("t1.i = %d\n", t1.getI()); // 栈空间初始值不定
printf("t1.j = %d\n", t1.getJ());
Test* pt = new Test; // 堆区对象
printf("pt->i = %d\n", pt->getI()); // 堆空间初始值不定
printf("pt->j = %d\n", pt->getJ());
delete pt;
return 0;
}
// 带有参数的构造函数,满足重载规律
class Test
{
public:
Test()
{
printf(“Test()\n”);
}
Test(int v)
{
printf(“Test(int v), v = %d\n”, v);
}
};
int main()
{
Test t; // 调用 Test()
Test t1(1); // 调用 Test(int v)
Test t2 = 2; // 调用 Test(int v)
int i(100);
printf("i = %d\n", i);
return 0;
}
11、类的成员 - 初始化列表
必须在类初始化列表中初始化的几种情况:
- 类成员为const或引用类型
- 类成员为类类型
- 子类调用父类构造函数
// 1.const和引用只能初始化不能赋值
class Base
{
private:
const int i;// const
int &j;// 引用
public:
Base(int v) : i(v), j(v) // 用列表初始化
{
}
void print_val()
{
cout << i << " " << j << endl;
}
};
int main(int argc ,char **argv)
{
Base b(100);
b.print_val();
}
// 2.通过显式调用父类构造函数,对父类数据成员初始化
#include <stdio.h>
#include
#include
using namespace std;
class Base
{
private:
int x;
int y;
int z;
public:
Base (int a, int b, int c)
{
cout << “Base” << a << " " << b << " " << c << endl;
};
};
class MyBase
{
private:
Base b; // 类类型
public:
MyBase(): b(1,2,3)// 列表初始化父类数据成员
{
cout << “MyBase” << endl;
};
};
int main()
{
MyBase b;// 1 2 3
// MyBase
return 0;
}
// 3. 如果类存在继承关系,子类必须在其初始化列表中调用父类的构造函数
class Base
{
private:
int a;
public:
Base(int a)
{
cout << “Base” << a << endl;
}
};
class Mybase : public Base// 继承关系
{
private:
int b;
public:
Mybase(int v) : Base(v) // 子类调用父类构造函数
{
b = v;
cout << “MyBase” << endl;
}
};
int main(int argc ,char **argv)
{
Mybase mybase(100); // 100
// MyBase
return 0;
}
// 成员的初始化顺序和声明顺序相同
// 初始化列表先于构造函数执行
class Value
{
private:
int mi;
public:
Value(int i)
{
printf("i = %d ", i);
}
};
class Test
{
private:// 声明顺序
Value m2;// 类类型
Value m3;
Value m1;
public:
Test() : m1(1), m2(2), m3(3) // 初始化列表
{
printf(“Test::Test()\n”);
}
};
int main()
{
Test t; // i = 2 i = 3 i = 1
// Test::Test()
return 0;
}
11、类的成员 - 拷贝构造函数
C++中三种情况下需要调用拷贝构造函数:
- 对象以值传递的方式传入函数参数
- 对象以值传递的方式从函数返回
- 一个对象用于初始化另一个对象
// 1.值传递给函数时会产生临时变量作为拷贝构造函数的实参
class CExample
{
private:
int a;
public:
CExample(int b)
{
a = b;
cout<<"creat: "<<a<<endl;
}
CExample(const CExample& C)
{
a = C.a;
cout<<“copy”<<endl;
}
~CExample()
{
cout<< "delete: "<<a<<endl;
}
void Show ()
{
cout<<a<<endl;
}
};
void g_Fun(CExample C) // 这里如果改成CExample& C引用就不会产出临时变量
{
cout<<“test”<<endl;
}
int main()
{
CExample test(11); // creat: 11
g_Fun(test); // copy
test
// delete: 11 这里两次调用析构,一次是析构临时变量
// delete: 11 一次析构对象
return 0;
}
// 2.返回对象时产生一个临时对象作为拷贝构造函数的实参
class Integer
{
public:
int m_val;
Integer()
{
cout<<"default Constructor"<<endl;
};
Integer(int i):m_val(i)
{
cout<<"Constructor"<<endl;
};
Integer(const Integer& arg)
{
this->m_val=arg.m_val;
cout<<"Copy Constructor"<<endl;
};
~Integer()
{
cout << "delete" << endl;
};
};
Integer testFunc(Integer inter)
{
inter.m_val++;
cout<<“before return”<<endl;
return inter;
}
int main(int argc,char* argv[])
{
Integer resutl; // default constructor 对象构造
resutl = testFunc(2); // Constructor 形参对象的构造 \
before return 进入函数 \
Copy Constructor返回临时对象,调用拷贝构造函数 \ delete 析构掉临时对象 \
delete 析构掉参数对象
cout<<resutl.m_val<<endl; // 3
// delete 析构掉resutl对象
return 0;
}
// 3.对象需要通过另外一个对象进行初始化
CExample A(100);
CExample B = A;
// 浅拷贝 深拷贝
// 没定义构造函数时,编译器自动提供无参构造函数和拷贝构造函数(有拷贝操作时)
// 自定义构造函数的话,两个构造函数必须同时指定
// 自定义构造函数,必然需要实现深拷贝
// 拷贝构造函数必须用引用传值,也就是传址,因为传址会造成再次调用构造函数,形成死循环
// 当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象
class Test
{
private:
int i;
int j;
int* p;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
int* getP()
{
return p;
}
Test(int v)// 构造函数
{
i = 1;
j = 2;
p = new int;
*p = v;
}
Test(const Test& t) // 深拷贝构造函数
{
i = t.i;
j = t.j;
p = new int;
*p = *t.p;
}
void free()
{
delete p;
}
};
int main()
{
Test t1(3);
Test t2(t1);
printf("t1.i = %d, t1.j = %d, *t1.p = %d\n", t1.getI(), t1.getJ(), *t1.getP());
printf("t2.i = %d, t2.j = %d, *t2.p = %d\n", t2.getI(), t2.getJ(), *t2.getP());
t1.free();
t2.free();
return 0;
}
12、类的成员 - 析构函数
// 用来销毁对象,没有参数 没有返回值 被自动调用
// 析构顺序由于全局、静态变量先于局部变量构造,首先析构局部变量,而手动析构在 } 之前,在由于全局变量作用域大于静态变量,先于静态变量构造,所以最后析构
class Test
{
private:
int mi;
public:
Test(int i)
{
mi = i;
printf(“Test(): %d\n”, mi);
}
~Test()
{
printf("~Test(): %d\n", mi);
}
};
Test C(1);
int main()
{
Test* A = new Test(4);
static Test D(2);
Test B(3);
delete A; // ~Test(): 4 \
~Test(): 3 \
~Test(): 2 \
~Test(): 1 \
return 0;
}
class Member
{
const char* ms;
public:
Member(const char* s)
{
printf(“Member(const char* s): %s\n”, s);
ms = s;
}
~Member()
{
printf("~Member(): %s\n", ms);
}
};
class Test
{
Member mA;
Member mB;
public:
Test() : mB(“mB”), mA(“mA”)
{
printf(“Test()\n”);
}
~Test()
{
printf("~Test()\n");
}
};
Member gA(“gA”);
int main()
{
Test t;
return 0;
}
13、类的成员 - 临时对象
临时对象产生主要有以下三种情况:
- 以值的方式给函数传参
- 函数返回一个对象时
- 隐式类型转换
// 值传递时会调用拷贝构造函数,并且创建临时对象;在多次继承情况下,临时对象的创建是一个很大的性能消耗,用引用传参可以避免
// 1.值传递给函数参数首先会产生临时变量,其次调用拷贝构造函数传递临时变量,然后临时变量进入函数进行相关操作,最后析构掉临时对象和对象
class CTemp
{
public:
int a;
int b;
public:
CTemp(int m, int n);
CTemp(const CTemp& t);
int GetSum(CTemp ts);
};
CTemp::CTemp(int m , int n)
{
printf(“Construct function!\n”);
a = m;
b = n;
printf(“a = %d, b = %d\n”,a,b);
}
CTemp::CTemp(const CTemp& t)
{
printf(“Copy function!\n”);
a = t.a;
b = t.b;
}
CTemp:: ~CTemp()
{
printf(“delete!\n”);
}
int CTemp::GetSum(CTemp ts)// 修改成CTemp& ts
{
int tmp = ts.a + ts.b;
ts.a = 1000; //此时修改的是tm的一个副本
return tmp;
}
int main()
{
CTemp tm(10,20);// Construct function! 调用构造函数
a = 10, b = 20
printf(“Sum = %d \n”,tm.GetSum™);// Copy function! 调用拷贝构造函数
Sum = 30
delete! 这里是析构临时变量
printf(“tm.a = %d \n”,tm.a);// tm.a = 10修改成引用tm.a = 1000
delete!
}
// 2.返回对象时产生一个临时对象作为拷贝构造函数的实参
class Integer
{
public:
int m_val;
Integer()
{
cout<<"default Constructor"<<endl;
};
Integer(const Integer& arg)
{
this->m_val=arg.m_val;
cout<<"Copy Constructor"<<endl;
};
~Integer()
{
cout << "delete" << endl;
};
};
Integer testFunc( )
{
Integer inter;
cout<<“before return”<<endl;
return inter;
}
int main(int argc,char* argv[])
{
testFunc(); // default constructor
before return
delete析构掉临时对象
return 0;
}
// 3.隐式类型转换生成临时对象
void print(string& str)
{
cout<<str<<endl;
}
print(“hello world”); // 如此调用会报错误
11、类的成员 – 虚函数和纯虚函数
// 虚函数可以被直接使用,也可以被子类重载后以多态形式调用
// 纯虚函数在子类中只有声明而没有定义,必须在子类中实现该函数,也可以在子类中重载,以多态形式被调用
class test
{
virtual string toString() = 0;// 纯虚函数
};
struct child:test
{
string toString() // 在子类中实现
{
return “ccc”;
}
};
struct sun:child
{
string toString()
{
return “sss”;
}
};
int main()
{
sun s;
child* c;
c = &s;
cout<<c->toString()<<endl; // sss
return 0;
}
14、类的成员 - 静态成员变量
// static成员变量是在类加载的时候生成
// 一般的成员变量是每个对象所自己拥有的,每次构造都会自己调用自己的成员变量
class Test
{
private:
int mCount;
public:
Test() : mCount(0)
{
mCount++;
}
~Test()
{
–mCount;
}
int getCount()
{
return mCount;
}
};
Test gTest;
int main()
{
Test t1;
Test t2;
printf("count = %d\n", gTest.getCount()); // 1
printf("count = %d\n", t1.getCount()); // 1
printf("count = %d\n", t2.getCount()); // 1
return 0;
}
// 实现多个对象之间的数据共享不会破坏隐藏的原则,保证了安全性还可以节省内存
// static静态成员变量属于整个类所有,先于对象存在,可以类名::变量直接访问
不隶属于任何一个单独的对象
需要在类外单独分配空间
储存在全局数据区
class Test
{
private:
static int cCount;// 不隶属于任何对象
public:
Test()
{
cCount++;
}
~Test()
{
–cCount;
}
int getCount()
{
return cCount;
}
};
int Test::cCount = 0;// 在类的外部单独定义,为其分配空间
Test gTest;
int main()
{
Test t1;
Test t2;
printf("count = %d\n", gTest.getCount()); // 3 统计某个类的对象数目
printf("count = %d\n", t1.getCount()); // 3
printf("count = %d\n", t2.getCount()); // 3
Test* pt = new Test(); // pt指向一个动态分配的,并初始化的无名对象
printf("count = %d\n", pt->getCount()); // 4
delete pt;
printf("count = %d\n", gTest.getCount()); // 3
return 0;
}
15、类的成员 - 静态成员函数
由于static修饰的类成员属于类,不属于对象。
因此static类成员函数是没有this指针的,this指针是指向本对象的指针。
正因为没有this指针,所以static类成员函数不能访问非static的类成员,只能访问 static修饰的类成员
class Text
{
public:
static int fun()
{
return num;
}
static int count;
int num;
};
int Text::count=5;// 用static成员变量必须要初始化
int main()
{
Text t1;
Text t2;
t1.num=100;
t1.fun(); //发生错误,fun函数return的是非static类成员 如果return count就正确
return 0;
}
// 静态成员函数不可以直接访问成员变量
// 可以通过类名直接调用静态函数
// static静态成员变量属于整个类所有,先于对象存在,可以类名::变量直接访问
class Test
{
private:
int a;
public:
Test():a(0)
{
}
int getData();
static void setData(Test& T, int i); // 静态成员函数不可以直接访问成员变量
};
int Test::getData()
{
return a;
}
void Test::setData(Test& T, int i)
{
T.a = i;
}
int main()
{
Test T;
printf("%d\n", T.getData());
T.setData(T, 2); // 可以通过类名
Test::setData(T, 2); // 可以通过类名直接调用静态函数
printf("%d\n", T.getData());
return 0;
}
16、二阶构造模式
一、关于构造函数
- 类的构造函数用于对象的初始化
- 构造函数与类文明相同,且没有返回值
- 构造函数在对象被创建时自动被调用
二、问题
1.如何判断构造函数的执行结果?
2.在构造函数中执行return语句会发生什么?
3.构造函数执行结束意味着对象构造成功?
// 第一阶段:资源无关的初始化的操作
// 第二阶段:需要使用系统资源的操作
class TwoPhaseCons
{
private:
TwoPhaseCons() // 第一阶段构造函数
{
}
bool construct() // 第二阶段构造函数
{
return true;
}
public:
static TwoPhaseCons* NewInstance(); // 对象创建函数
};
TwoPhaseCons* TwoPhaseCons::NewInstance()
{
TwoPhaseCons* ret = new TwoPhaseCons();
if( !(ret && ret->construct()) ) // 若第二阶段构造失败,返回 NULL
{
delete ret;
ret = NULL;
}
return ret;
}
int main()
{
TwoPhaseCons* obj = TwoPhaseCons::NewInstance();// 只能被内部自动调用
printf("obj = %p\n", obj);
delete obj;
return 0;
}
//
#include “IntArray.h”
#include <stdio.h>
IntArray::IntArray()//1.
{
}
bool IntArray::IntArrayConstruct()//2.
{
m_Array = new int[5];
for(int i=0; i<5; i++)
{
m_Array[i] = 0;
}
return true;
}
IntArray* IntArray::NewIntArray()//3.
{
IntArray* ret = new IntArray();
if(!(ret && ret->IntArrayConstruct()))
{
delete ret;
ret = 0;
}
return ret;
}
bool IntArray::setArray(int index, int value)
{
if( (0 <= index) && (index < 5) )
m_Array[index] = value;
return 0;
}
bool IntArray::getArray()
{
for(int i=0; i<5; i++)
{
printf("%d\n",*(m_Array+i));
}
return 0;
}
IntArray::~IntArray()
{
delete []m_Array;
}
////////////////////
#ifndef INTARRAY_H
#define INTARRAY_H
class IntArray
{
private:
int* m_Array;
IntArray();
bool IntArrayConstruct();
// ~IntArray();
public:
static IntArray* NewIntArray();
bool setArray(int index, int value);
bool getArray();
~IntArray();
};
#endif
//////////////////
#include <stdio.h>
#include “IntArray.h”
int main()
{
IntArray* A = IntArray::NewIntArray();
A->setArray(3, 22);
A->getArray();
delete A;
return 0;
}
17、友元函数
// 友元函数可以直接访问类的所有成员
// 破坏了C++的封装性
#include <stdio.h>
#include <math.h>
class Point
{
private:
double x;
double y;
public:
Point(double x, double y)
{
this->x = x;
this->y = y;
}
double getX()
{
return x;
}
double getY()
{
return y;
}
friend double func(Point& p11, Point& p22);
};
double func(Point& p11, Point& p22) // 全局函数
{
double ret = 0;
ret = (p22. getY() - p11. getY()) * (p22. getY() - p11 getY()y) +
(p22. getX() - p11. getX()) * (p22. getX() - p11. getX());
ret = (p22.y - p11.y) * (p22.y - p11.y) + // 直接调用
(p22.x - p11.x) * (p22.x - p11.x);
ret = sqrt(ret);
return ret;
}
int main()
{
Point p1(1, 2);
Point p2(10, 20);
printf("p1(%f, %f)\n", p1.getX(), p1.getY());
printf(“p2(%f, %f)\n”, p2.getX(), p2.getY());
printf("|(p1, p2)| = %f\n", func(p1, p2));
return 0;
}
18、重载 - 类中函数的重载
// 类中的成员函数都可以重载(构造函数 普通成员函数 静态成员函数)
class Test
{
int i;
public:
/构造函数重载********/
Test()
{
printf(“Test::Test()\n”);
this->i = 0;
}
Test(int i)
{
printf(“Test::Test(int i)\n”);
this->i = i;
}
Test(const Test& obj)
{
printf(“Test(const Test& obj)\n”);
this->i = obj.i;
}
/静态成员函数重载********/
static void func()
{
printf(“void Test::func()\n”);
}
void func(int i)
{
printf("void Test::func(int i), i = %d\n", i);
}
int getI()
{
return i;
}
};
/普通函数重载********/
void func()
{
printf(“void func()\n”);
}
void func(int i)
{
printf(“void func(int i), i = %d\n”, i);
}
int main()
{
Test t; // Test::Test()
Test t1(1); // Test::Test(int i)
Test t2(t1); // Test(const Test& obj)
cout<<endl;
t1.func(); // void Test::func()
t1.func(2); // void Test::func(int i), i = 2
cout<<endl;
func();
func(1);
return 0;
}
// 重载函数可以扩展其功能
char* strcpy(char* buf, const char* str, unsigned int n)
{
return strncpy(buf, str, n);
}
int main()
{
const char* s = “D.T.Software”;
char buf[8] = {0};
//strcpy(buf, s);
strcpy(buf, s, sizeof(buf)-1);
printf("%s\n", buf);
return 0;
}
19、重载 – 运算符的重载
// 运算符重载的本质是一个函数
// 两个数相加相当于调用下面的函数:
int operator + (int a, int b) // 返回值为int类型
{return (a+b)}
///////////////////////////Complex.h///////////////////////////
#ifndefCOMPLEX_H
#defineCOMPLEX_H
class Complex
{
private:
int a;
int b;
public:
int getA();
int getB();
Complex(int, int) // 这里为什么必须要初始化a b
friend Complex operator + (const Complex& p1, const Complex& p2);
};
#endif
///////////////////////////Complex.cpp///////////////////////////
#include <stdio.h>
#include “Complex.h”
Complex(int a = 0, int b = 0) // 这里为什么必须要初始化a b
{
this->a = a;
this->b = b;
}
int Complex::getA()
{
return a;
}
int Complex::getB()
{
return b;
}
Complex operator + (const Complex& p1, const Complex& p2) // 全局的成员函数
{
int a = p1.a + p2.a;
int b = p1.b + p2.b;
return Complex(a, b);
}
///////////////////////////main.cpp/////////////////////////////
#include <stdio.h>
#include “Complex.h”
int main()
{
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2;
printf(“c3.a = %d, c3.b = %d\n”, c3.getA(), c3.getB());
return 0;
}
// 操作数函数也可以通过成员函数来完成
// 当全局函数和成员函数同时存在时 优先调用成员函数
// 二元运算符成员函数重载时, 只需要一个参数, 另一个参数由this(左操作数)指针传入
#include <stdio.h>
#include
#include
using namespace std;
class Complex
{
private:
int a;
int b;
public:
Complex(int , int);
int getA();
int getB();
Complex operator + (const Complex& p);
};
Complex::Complex(int x = 0, int y = 0)
{
this->a = x;
this->b = y;
}
int Complex::getA()
{
return a;
}
int Complex::getB()
{
return b;
}
Complex Complex::operator + (const Complex& p) // 第二个参数由this传入
{
Complex ret;
ret.a = this->a + p.a;
ret.b = this->b + p.b;
return ret;
}
int main()
{
Complex c1(1, 2);// 调用构造函数初始化
Complex c2(3, 4);
Complex c3 = c1 + c2; // c1.operator + (c2)
cout << c3.getA() << "." << getB() << endl;
return 0;
}
21、重载 - 赋值操作符的重载
// 当类的成员指代了外部资源,就需要重载赋值操作符来完成深拷贝
#include
#include
using namespace std;
class Test
{
int* m_pointer;
public:
Test()
{
m_pointer = NULL;
}
Test(int i)
{
m_pointer = new int(i);
}
Test(const Test& obj)
{
m_pointer = new int(*obj.m_pointer);
}
Test& operator = (const Test& obj)
{
if( this != &obj )
{
delete m_pointer;
m_pointer = new int(*obj.m_pointer);
}
return *this;
}
void print()
{
cout << "m_pointer = " << hex << m_pointer << endl;
}
~Test()
{
delete m_pointer;
}
};
int main()
{
Test t1 = 1;
Test t2;
t2 = t1;
t1.print();
t2.print();
return 0;
}
22、重载 - 数组操作符的重载
// 只能通过类的成员函数重载
// 重载函数能且仅能使用一个参数
// 可以定义不同参数的多个重载函数
#include
#include
using namespace std;
class Test
{
int a[5];
public:
int& operator [] (int i)
{
return a[i];
}
int& operator [] (const string& s)
{
if( s == "1st" )
{
return a[0];
}
else if( s == "2nd" )
{
return a[1];
}
else if( s == "3rd" )
{
return a[2];
}
else if( s == "4th" )
{
return a[3];
}
else if( s == "5th" )
{
return a[4];
}
return a[0];
}
int length()
{
return 5;
}
};
int main()
{
Test t;
for(int i=0; i<t.length(); i++)
{
t[i] = i;
}
for(int i=0; i<t.length(); i++)
{
cout << t[i] << endl;
}
cout << t["5th"] << endl;
cout << t["4th"] << endl;
cout << t["3rd"] << endl;
cout << t["2nd"] << endl;
cout << t["1st"] << endl;
return 0;
}
21、字符串类 - string
1、cin
2、cin.get()
3、cin.getline()
4、getline()
5、gets()
6、getchar()
1、cin>>
// >> 是会过滤掉不可见字符(如 空格 回车,TAB 等)
main ()
{
char a[20];
cin>>a>>b;
cout<<a+b<<endl;
}
输入:2[回车]3[回车]
输出:5
输入:jkljkl jkljkl //遇空格结束
输出:jkljkl
2、cin.get()
// 用来接收一行字符串,可以接收空格
main ()
{
char a[20];
cin.get(a,20);
cout << a << endl;
}
输入:jkl jkl jkl
输出:jkl jkl jkl
输入:abcdeabcdeabcdeabcdeabcde (输入25个字符)
输出:abcdeabcdeabcdeabcd (接收19个字符+1个’\0’)
3、cin.getline()
// 接受一个字符串,可以接收空格并输出
main ()
{
char m[20];
cin.getline(m,5);
cout<<m<<endl;
}
输入:jkljkljkl
输出:jklj
4、getline()
// 接受一个字符串,可以接收空格并输出,需包含“#include”
// cin.getline()属于istream流,而getline()属于string流,是不一样的两个函数
main ()
{
string str;
getline(cin, str);
cout<<str<<endl;
}
输入:jkljkljkl
输出:jkljkljkl
输入:jkl jfksldfj jklsjfl
输出:jkl jfksldfj jklsjfl
// substr(size_type _Off = 0,size_type _Count = npos) const;
该系统函数返回被截后的子字符串,它接受2个必选参数,参数1为要截取的字符串,参数2为截取的开始位置,参数3可选,表示截取长度。
int main()
{
string s(“12345asdf”);
string a = s.substr(0,5); //获得字符串s中从第0位开始的长度为5的字符串
cout << a << endl; // 12345
}
#include
#include
using namespace std;
void string_sort(string* a, int len)
{
for(int i=0; i<len; i++)
{
for(int j=i; j<len; j++)
{
if( a[i] > a[j] )
{
swap(a[i], a[j]);
}
}
}
}
string string_add(string* a, int len)
{
string ret = “”;
for(int i=0; i<len; i++)
{
ret += a[i] + "; ";
}
return ret;
}
int main()
{
string sa[7] =
{
“Hello World”,
“D.T.Software”,
“C#”,
“Java”,
“C++”,
“Python”,
“TypeScript”
};
string_sort(sa, 7);
for(int i=0; i<7; i++)
{
cout << sa[i] << endl;
}
cout << endl;
cout << string_add(sa, 7) << endl;
return 0;
}
// 字符串和数字的转换
#include
#include
#include
using namespace std;
#define TO_NUMBER(s, n) (istringstream(s) >> n)
#define TO_STRING(n) (((ostringstream&)(ostringstream() << n)).str())
int main()
{
double n = 0;
if( TO_NUMBER("234.567", n) )
{
cout << n << endl;
}
string s = TO_STRING(12345);
cout << s << endl;
return 0;
}
// 字符串右移
#include
#include
using namespace std;
string operator >> (const string& s, unsigned int n)
{
string ret = “”;
unsigned int pos = 0;
n = n % s.length();
pos = s.length() - n;
ret = s.substr(pos);
ret += s.substr(0, pos);
return ret;
}
int main()
{
string s = “abcdefg”;
string r = (s >> 3);
cout << r << endl;
return 0;
}
23、函数对象
// 函数对象的意义是取代函数指针
#include
#include
using namespace std;
int fib()
{
static int a0 = 0;
static int a1 = 1;
int ret = a1;
a1 = a0 + a1;
a0 = ret;
return ret;
}
int main()
{
for(int i=0; i<10; i++)
{
cout << fib() << endl;
}
cout << endl;
for(int i=0; i<5; i++)
{
cout << fib() << endl;
}
return 0;
}
///////////////////////////////////////////
#include
#include
using namespace std;
class Fib
{
int a0;
int a1;
public:
Fib()
{
a0 = 0;
a1 = 1;
}
Fib(int n)
{
a0 = 0;
a1 = 1;
for(int i=2; i<=n; i++)
{
int t = a1;
a1 = a0 + a1;
a0 = t;
}
}
int operator () () //
{
int ret = a1;
a1 = a0 + a1;
a0 = ret;
return ret;
}
};
int main()
{
Fib fib;
for(int i=0; i<10; i++)
{
cout << fib() << endl;
}
cout << endl;
for(int i=0; i<5; i++)
{
cout << fib() << endl;
}
cout << endl;
Fib fib2(10);
for(int i=0; i<5; i++)
{
cout << fib2() << endl;
}
return 0;
}
24、智能指针
// 重载->和* 解决内存泄漏
// 指针的生命周期结束时主动释放堆空间
// 一片堆空间只能由一个指针标识
// 杜绝指针比较和指针运算
#include
#include
using namespace std;
class Test
{
int i;
public:
Test(int i)
{
this->i = i;
}
int value()
{
return i;
}
~Test()
{
}
};
int main()
{
for(int i=0; i<5; i++)
{
Test* p = new Test(i);
cout << p->value() << endl;
}
return 0;
}
#include
#include
using namespace std;
class Test
{
int i;
public:
Test(int i)
{
cout << “Test(int i)” << endl;
this->i = i;
}
int value()
{
return i;
}
~Test()
{
cout << “~Test()” << endl;
}
};
class Pointer
{
Test* mp;
public:
Pointer(Test* p = NULL)
{
mp = p;
}
Pointer(const Pointer& obj)
{
mp = obj.mp;
const_cast<Pointer&>(obj).mp = NULL;
}
Pointer& operator = (const Pointer& obj)
{
if( this != &obj )
{
delete mp;
mp = obj.mp;
const_cast<Pointer&>(obj).mp = NULL;
}
return *this;
}
Test* operator -> ()
{
return mp;
}
Test& operator * ()
{
return *mp;
}
bool isNull()
{
return (mp == NULL);
}
~Pointer()
{
delete mp;
}
};
int main()
{
Pointer p1 = new Test(0);
cout << p1->value() << endl;
Pointer p2 = p1;
cout << p1.isNull() << endl;
cout << p2->value() << endl;
return 0;
}
25、类型转换
// explicit用于杜绝隐式转换
// 转换构造函数(有且仅有一个参数 参数是基本类型或者其他类型)
#include
#include
using namespace std;
class Test
{
int mValue;
public:
Test()
{
mValue = 0;
}
explicit Test(int i)
{
mValue = i;
}
Test operator + (const Test& p)
{
Test ret;
ret = mValue + p.mValue;
return ret;
}
int value()
{
return mValue;
}
};
int main()
{
Test t;
Test r;
t = static_cast<Test>(5); // t = Test(5);
r = t + static_cast<Test>(10); // r = t + Test(10);
cout << r.value() << endl;
return 0;
}
// 类型转换函数
26、继承 – 概念
// 继承父类的成员函数中,调用的变量也来自父类,隐藏但不会覆盖,他们同时存在
class Parent
{
public:
string mv;
Parent():mv("Parent")
{
cout << "Parent()" << endl;
}
void method()
{
cout << "mv = " << mv << endl;
}
};
class Child : public Parent
{
public:
string mv;
void hello()
{
cout << "I'm Child class!" << endl;
}
};
int main()
{
Child c;
c.hello();
c.method(); // 成员函数继承自父类 成员函数的变量也是父类的(虽然被隐藏)
return 0;
}
27、继承 – protected
// protected是为了对外界隐藏敏感信息,不能被外界直接访问,可以被子类直接访问
class Parent
{
protected:
int mv;
public:
Parent()
{
mv = 100;
}
int value()
{
return mv;
}
};
class Child : public Parent
{
public:
int addValue(int v)// 子类可以在直接访问protected变量
{
mv = mv + v;
}
};
int main()
{
Parent p;
cout << "p.mv = " << p.value() << endl;// 100
// p.mv = 1000; // error 外界不能访问
Child c;
cout << "c.mv = " << c.value() << endl; // 100
c.addValue(50);
cout << "c.mv = " << c.value() << endl; // 150
// c.mv = 10000; // error main属于类的外部
return 0;
}
27、继承 – 构造和析构
// 析构函数应定义为虚函数
// 如果父类析构函数没有定义成虚函数,则编译器实现静态绑定,删除父类对象只调用父类的析构函数,二不析构子类对象,造成内存泄露。
class Base
{
public:
Base()
{
cout << “Base()” << endl;
}
virtual ~Base()
{
cout << “~Base()” << endl;
}
};
class Expend :public Base
{
public:
Expend()
{
cout << “Expend()” << endl;
}
~Expend()
{
cout << “~Expend()” << endl;
}
};
int main()
{
Base *pd = new Expend; // Base()
Expend()
delete pd; // ~Expend() 父类析构函数不加virtual不会执行这一行
~Base()
}
// 先调用父类构造函数
// 再调用成员变量的构造函数
// 最后调用自身的构造函数
class Object
{
private:
string ms;
public:
Object(string s)
{
cout << "Object(string s) : " << s << endl;
ms = s;
}
virtual ~Object()
{
cout << "~Object() : " << ms << endl;
}
};
class Parent : public Object
{
private:
string ms;
public:
Parent() : Object(“Default”)
{
cout << “Parent()” << endl;
ms = “Default”;
}
Parent(string s) : Object(s)
{
cout << "Parent(string s) : " << s << endl;
ms = s;
}
~Parent()
{
cout << "~Parent() : " << ms << endl;
}
};
class Child : public Parent
{
private:
Object mO1;
Object mO2;
string ms;
public:
Child() : mO1(“Default 1”), mO2(“Default 2”)
{
cout << “Child()” << endl;
ms = “Default”;
}
Child(string s) : Parent(s), mO1(s + " 1"), mO2(s + " 2")
{
cout << "Child(string s) : " << s << endl;
ms = s;
}
~Child()
{
cout << "~Child() " << ms << endl;
}
};
int main()
{
Child cc(“cc”); // Object(string s) : cc \ 先父母
Parent(string s) : cc
Object(string s) : cc + 2 \ 后客人
Object(string s) : cc + 1 \ 再自己
Child(string s) : cc ;
cout << endl;
// ~Child() cc
~Object() : cc 1
~Object() : cc 2
~Parent() : cc
~Object() : cc
return 0;
}
28、继承 – 重定义(隐藏)
// 指在不同的作用域中,函数名相同,不能构成重写的都是重定义
// 子类的成员变量将隐藏父类中的同名成员变量
// 默认访问的是子类的成员,继承了但是不能直接访问,想访问父类用 ::分辨
class Parent
{
public:
int mi;
Parent()
{
cout << "Parent() : " << "&mi = " << &mi << endl;
}
};
class Child : public Parent
{
public:
int mi;
Child()
{
cout << "Child() : " << "&mi = " << &mi << endl;
}
};
int main()
{
Child c;
c.mi = 100; // 访问子类的变量
c.Parent::mi = 1000;// 访问父类的变量
cout << "&c.mi = " << &c.mi << endl; // 0xbfb04e28
cout << "c.mi = " << c.mi << endl; // 100
cout << "&c.Parent::mi = " << &c.Parent::mi << endl; // 0xbfb04e24
cout << "c.Parent::mi = " << c.Parent::mi << endl; // 1000
return 0;
}
// 子类的函数将隐藏父类的同名函数
// 由于再不同的作用域,子类不能重载父类的同名函数,用作用域分辨符::区分
class Parent
{
public:
int mi;
void add(int v)
{
mi += v;
cout << “Child::add(int)” << endl; }
void add(int a, int b)
{
mi += (a + b);
cout << "Parent::add(int, int) " << endl;
}
};
class Child : public Parent
{
public:
int mi;
void add(int x, int y, int z)
{
mi += (x + y + z);
}
};
int main()
{
Child c;
c.Parent::mi = 1000;
c.mi = 100;
c.add(1); // 编译不通过 子类同名函数隐藏父类,不存在重载
c. Parent::add(2, 3); // Parent::add(int, int)
c.add(4, 5, 6); // Child::add(int, int, int)
cout << "c.Parent::mi = " << c.Parent::mi << endl;// 1005
cout << "c.mi = " << c.mi << endl;// 116
return 0;
}
29、继承 – 赋值兼容
// 面向对象赋值兼容规则
1.子类对象可以赋值给父类对象
2.子类对象可以初始化父类对象
3.父类指针可以指向子类对象
4.父类引用可以引用子类对象
// 当父类指针(引用)指向子类对象时,子类对象退化成父类对象,只能访问父类中定义的成员
class Parent
{
public:
int mi;
void add(int i)
{
mi += i;
}
void add(int a, int b)
{
mi += (a + b);
}
};
class Child : public Parent
{
public:
int mv;
void add(int x, int y, int z)
{
mv += (x + y + z);
}
};
int main()
{
Parent p;
Child c;
p = c; // 1.子类对象可以赋值给父类对象
Parent p1(c); // 2.子类对象可以初始化父类对象
Parent& rp = c; // 3.父类引用可以引用子类对象
Parent* pp = &c; // 4.父类指针可以指向子类对象
rp.mi = 100;
rp.add(5); // 子类对象c已经退化成父类对象
rp.add(10, 10);
/* 为什么编译不过? */
// pp->mv = 1000;
// pp->add(1, 10, 100);
return 0;
}
// 重写父类函数,不加virtual的话编译器默认指向父类
class Parent
{
public:
void print()
{
cout << “I’m Parent.” << endl;
}
};
class Child : public Parent
{
public:
void print()
{
cout << “I’m Child.” << endl;
}
};
int main()
{
Child c;
Parent *p;
p = &c;
p->print();// I’m Parent. 默认指向父类函数
return 0;
}
30、继承 – 重写(覆盖 多态)
// 当在子类中定义了一个与父类完全相同的虚函数时,则称子类的这个函数重写了父类这个虚函数。
// 根据实际指针指向的对象判断调用函数
// 子类重写父类函数,必须用virtual修饰,不然没有意义。因为默认调用父类函数
// 静态联编:在程序的编译期就能确定函数的具体调用 重载
// 动态联编:在程序的执行期才能确定函数的具体调用 重写
class Parent
{
public:
virtual void print()
{
cout << “I’m Parent.” << endl;
}
};
class Child : public Parent
{
public:
void print()
{
cout << “I’m Child.” << endl;
}
};
void how_to_print(Parent* p)
{
p->print(); // 多态性 根据p指向的对象不同,有不同的输出结果
}
int main()
{
Parent p;
Child c;
p.print(); // I'm Parent. 静态联编
c.print();// I’m Child.静态联编
cout<<endl;
how_to_print(&p); // I'm Parent. 动态联编
how_to_print(&c); // I'm Child. 动态联编
return 0;
}
2、泛型编程 – 单例类模板
//
2、泛型编程 – 函数模板
// template
// 函数模板是一种特殊的函数,可用不同类型进行调用, 类型可被参数化
#include
#include
using namespace std;
template < typename T >
void Swap(T& a, T& b)
{
T c = a;
a = b;
b = c;
}
int main()
{
int a = 0;
int b = 1;
Swap(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << endl;
double x = 1.234;
double y = 4.321;
Swap(x, y); // 隐式调用
cout << "x = " << x << endl;
cout << "y = " << y << endl;
cout << endl;
string m = "Dazhang";
string n = "yu";
Swap<string>(m, n); // 显式调用
cout << "m = " << m << endl;
cout << "n = " << n << endl;
cout << endl;
return 0;
}
// template <typename T1, typename T2, typename T3>
// 多参数函数模板无法自动推导返回值类型,第一个参数为返回值类型,且必须
#include
#include
using namespace std;
template < typename T1, typename T2, typename T3 >
T1 Add(T2 a, T3 b)
{
return static_cast(a + b);
}
int main()
{
// T1 = int, T2 = double, T3 = double
int r1 = Add(0.5, 0.8);
// T1 = double, T2 = float, T3 = double
double r2 = Add<double, float>(0.5, 0.8);
// T1 = float, T2 = float, T3 = float
float r3 = Add<float, float, float>(0.5, 0.8);
cout << "r1 = " << r1 << endl; // r1 = 1
cout << "r2 = " << r2 << endl; // r2 = 1.3
cout << "r3 = " << r3 << endl; // r3 = 1.3
return 0;
}
// 编译器优先选用普通函数
// 可通过空模板实参列表限定值匹配模板
#include
#include
using namespace std;
template < typename T >
T Max(T a, T b)
{
cout << “T Max(T a, T b)” << endl;
return a > b ? a : b;
}
int Max(int a, int b)
{
cout << “int Max(int a, int b)” << endl;