目录
一、C语言知识
一、C语言知识
1.自增自减
a=i++是先赋值,然后再自增;a=++i是先自增,后赋值。
2. extern(外部)
- 单个文件内声明extern a,表示把全局变量a的作用域扩展到当前。
- 多个文件之间:只在源文件C中定义了全局变量int a,那么在文件B中声明extern int a,就可以用到a的值了。
- 替代方法1:在源文件C内定义全局变量int a,在其头文件H中声明extern int a,那么其他文件B里只要include过,就可以用前者的变量或函数
- 替代方法2:在源文件C中定义了Li,又在头文件H内声明了struct student Li,那么其他文件B里只要include过,就可以用前者的变量或函数
- 定义变量或函数时默认缺省了extern,否则用static加以限制作用域。
3. 头文件写法
#ifndef _add_H_
#define _add_H_
int add(int x, int y);
#endif
4. include
哪里需要就可以在哪里include,同名的头文件中include的库,源文件中不必重复include。
二、C++
(一)一些说明
1.内联函数
在函数调用处直接嵌入函数体的函数称为内联函数,内联函数一般不使用。
2. 函数重载
3. 引用(别名)
int &r=n;
//引用n 相当于r的别名,对n的修改等于对r的修改,除非加上const。
4. string类
string模板类:typedef basic_string<char> string
子串:
rfind //反方向寻找
还有:erase、replace、insert、c_str
5. 字符串的定义和引用方法
(二)类和对象
类的示例程序:
头文件:
#ifndef _MYCLASS_H_
#define _MYCLASS_H_
class A {
public:
//成员函数
void show();
double getscore() const; //常成员函数,不能修改成员变量
void setage(int age); //设置成员变量的值
void setscore(double score); //设置成员变量的值
//友元函数(说明这是外来函数,可以使用本类中的所有成员。
friend double adds(A* adult, double extra);
//构造函数
A(); //无参构造函数
A(const char* name, int sys, const char* school = "北大");
A(const char* name,int sys, int age = 25, double score = 99); //构造函数重载
A(int sys=100);
//成员变量
const int m_sys; //常成员变量,值定义后不能被修改,只能通过初始化参数列表的构造函数初始化
private:
const char* m_name;
int m_age;
double m_score;
const char* m_school; //普通的成员变量只能通过实例化的对象来访问
static int m_number;//静态成员变量,定义要在类外。通过类和对象都能访问。
//静态成员函数只能访问同类的静态成员变量
};
#endif
源文件:
#include "myClass.h"
#include <iostream>
using namespace std;
int A::m_number = 0;//静态成员变量的初始化必须在类外
//******///1构造函数
A::A(const char* name,int sys, int age, double score): m_name(name), m_sys(sys) {//构造函数的重载
m_age = age;
m_score = score;
m_number++;
m_school = "无";
}
A::A(const char* name, int sys, const char* school) :m_name(name), m_age(25), m_score(99), m_school(school) , m_sys(sys) {
//构造函数初始化列表
m_number++;
}
A::A(int sys) : m_sys(sys) { //只能通过初始化参数列表表来初始化常成员变量
m_name = "小明";
m_age = 23;
m_score = 95;
m_number++;//每输入一个信息,计数一次
m_school = "无";
}
//**************//2成员函数
void A::show() {
cout << m_name << "的年龄是" << m_age << ",学校是" << m_school << endl;
cout << "现在有" << m_number << "个人"<<endl;
}
double A::getscore () const
{
return m_score;//可读不可写
}
void A::setage(int age) {
m_age = age;
}
void A::setscore(double score) {
m_score = score;
}
double adds(A* adult, double extra)
{
return adult->A::m_score + extra;
}
//**************//3成员函数
主函数:
#include <iostream>
#include "myClass.h"
using namespace std;
int main()
{
cout << "你好" << endl;
//在栈上创建对象
A peer0(100); //调用无参构造函数
peer0.show();
A peer2("小王",100,26); //后面的参数使用构造函数的默认参数
A peer3("小王", 100,26,99.6);
A peer4("小王",100, "南航"); //构造函数的重载
peer4.show();
A* p = new A(100); //在堆上创建动态对象
p->setage(29); //使用成员函数给私有的成员变量赋值
p->show();
cout<<p->getscore()<<endl; //使用常成员函数读取私有成员变量
//通过友元函数加分
cout << adds(p, 62.3) << endl;
//在堆上动态创建对象
system("pause");
return 0;
}
运行结果:
1.成员对象和封闭类
封闭类:有成员对象的类
声明外部可用用extern
构造函数:名字和类的名字相同,初始化的作用。
复制构造函数:用一个对象去初始化同类的另一个对象。
2.友元(这是我朋友,可以用我的东西)
(1)友元函数:一个类的友元函数可以访问该类的私有成员
- 形式(是一个声明、过渡)
- 在一个类里说明一个非成员函数是该类的友元函数,则在外部的非成员函数中可以访问那个类的私有成员。
- 可以将一个类的成员函数(包括构造、析构函数)说明为另一个类的友元:
在类A中说明类B的一个成员函数为类A的友元,则在类B的那个成员函数中,可以直接访问类A的私有成员。
(2)友元类:如果A是B的友元类,那个A的成员函数可以访问B的私有成员:
- 在类A中声明B是类A的友元类,则在类B中可以访问类A的私有成员
(友元类之间的关系不能传递,不能继承)
3.动态内存分配
p=new T
p=new T[n] p是一个指针,T为类型名
delete p
(三)继承和派生(子类继承、增改父类的成员)
继承示例程序:
头文件 myInherit.h
#ifndef _MYINHERIT_H_
#define _MYINHERIT_H_
class A
{
public:
A(int len);
void show();
int adds(int ext);
protected:
int m_len;
};
class B :public A {
public:
B(int len, int num);//
void show(double a, double b);
//只要函数名字相同,就会修改继承的成员函数
private:
int m_num;
};
#endif
源文件 myInherit.cpp
#include "myInherit.h"
#include <iostream>
using namespace std;
//*******************A
A::A(int len):m_len(len) {}
void A::show()
{
cout << "A的len为" << m_len << endl;
}
int A::adds(int ext)
{
return m_len+ext;
}
//******************B
B::B(int len, int num) : A(len), m_num(num){} //调用基类A的构造函数
void B::show(double a, double b)
{
cout << "B的len为" << m_len ;
cout << ",B两数之和为" << a + b << endl;
}
主文件 main.cpp
#include <iostream>
#include "myInherit.h"
using namespace std;
int main()
{
A* p = new A(20);
p->show();
B* q = new B(30, 0);
q->show(3,4); //用的派生类的成员函数
p = q; //基类指针指向派生类,只能改变成员变量的值,不能使用成员函数
p->show();
cout << endl;
A xxl(1);
xxl.show();
B yyl(2, 3);
yyl.show(7,12);
xxl = yyl; //派生类向基类赋值,不能反过来,因为少值不够赋给多值
xxl.show(); //基类的私有变量m_len是派生类的值,但是调用的函数却是基类的。
//总结:编译器通过指针来访问成员变量,指针指向哪个对象就使用哪个对象的数据;
// 编译器通过指针的类型来访问成员函数,指针属于哪个类的类型就使用哪个类的函数。
//除非虚函数
system("pause");
return 0;
}
运行结果:
1.继承和派生(代码的重用性)
继承:在定义一个新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类,而吧B作为基类的一个派生类(也称子类)。
- 派生类是通过对基类进行修改和扩充得到的。在派生类中,可以扩充新的成员变量和成员函数。派生类一经定义后,可以独立使用,不依赖于基类。
- 派生类名y拥有基类的全部成员函数和成员变量,在派生类的各个成员函数中,不能访问基类中的private成员。
写法
class 派生类名:public基类名
{
};
内存空间:在派生类对象中,包含着基类对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前。
2. 继承关系和复合关系
类之间的两种关系:
(1)是:一个B对象也是一个A对象
(2)有:类C中“有”成员变量k,k是类D的对象,则C和D是复合关系,D对象是C对象的固有属性或组成部分。
3. 派生类覆盖基类成员
覆盖:派生类可以定义一个和基类成员同名的成员,叫做覆盖。在派生类中访问这类成员时,缺省的情况是访问派生类中定义的成员。要在派生类中访问由基类定义的同名成员时,要使用作用域符号::。
类的保护成员:
类除了公有成员和私有成员外,还有保护成员。派生类的成员函数可以访问当前对象的基类的保护成员,但是私有成员不能。
派生类的构造函数:
在创建派生类的对象时,需要调用基类的构造函数:初始化派生类对象中从基类继承的成员。在执行一个派生类的构造函数之前,总是先执行基类的构造函数。
调用时有显示和隐式(默认构造函数)
4. 公有继承的赋值兼容规则
直接基类与间接基类:
在声明派生类时,只需要列出它的直接基类。
构造函数从最顶层的基类开始执行。
(四)多态和虚函数(基类用指针调用子类的成员)
多态示例程序:
什么时候用?首先看成员函数所在的类是否会作为基类。然后看成员函数在类的继承后有无可能被更改功能,如果希望更改其功能的,一般应该将它声明为虚函数。
头文件 myVirtual.h
#ifndef _MYVIRTUAL_H_
#define _MYVIRTUAL_H_
class A
{
public:
A(int len);
virtual void show(double a,double b); //基类虚函数必须有关键字
protected:
int m_len;
};
class B :public A {
public:
B(int len, int num);//
void show(double a, double b); //虚函数的原型必须一模一样
private:
int m_num;
};
#endif
源文件 myVirtual.cpp
#include "myVirtual.h"
#include <iostream>
using namespace std;
//*******************A
A::A(int len):m_len(len) {}
void A::show(double a, double b)
{
cout << "A的len为" << m_len <<"和为" <<a+b<< endl;
}
//******************B
B::B(int len, int num) : A(len), m_num(num){} //调用基类A的构造函数
void B::show(double a, double b)
{
cout << "B的len为" << m_len <<"乘积为"<<a * b<<endl;
}
主文件 main.cpp
#include <iostream>
#include "myVirtual.h"
using namespace std;
int main()
{
A* p = new A(20);
p->show(55,66);
B* q = new B(30, 0);
q->show(44,77); //用的派生类的成员函数
p = q; //基类指针指向派生类,只会改变指针指向,不会改变其对象的值
//或者简单的A*p=new B(30,0);
p->show(55,66);
system("pause");
return 0;
}
运行结果:
1. 虚函数和多态
class base{
virtual int get();
};
int base::get(){}//写函数体时不用
多态:
- 派生类的指针可以赋给基类指针。通过基类指针调用基类和派生类中的同名虚函数时,若该指针指向一个基类的对象,那么被调用的是基类的虚函数;若指针指向一个派生类的对象,那么被调用的是派生类的虚函数。这种机制叫做“多态”
- 派生类的对象可以赋给基类引用。通过基类引用调用基类和派生类中的同名虚函数时,若该引用引用的是一个基类的对象,那么被调用的是基类的虚函数,派生类亦然。
除了构造函数和析构函数,在派生类中和基类中与虚函数同名同参数表的函数,不加virtual也自动成为虚函数。
2. 多态的实现原理
动态连编——编译时不确定调用的是基类的还是派生类的函数,运行时才能确定。
虚函数表。放在前几个字节中
3. 纯虚函数、抽象类、虚析构函数
一般有虚函数时,把析构函数也写成虚函数。
纯虚函数(没有函数体的):
virtual void p()=0;
(五)运算符重载、模板(万物归一)
重载和模板示例程序:
1.运算符重载
(1)实质是函数的重载
返回值类型 operator 运算符(形参表)
{
……
}
例:
class complex{
public:
double real,image;
complex operato-(const complex &c);//作为成员函数
};
complex operator+(const complex &a, const complex &b){
return complex (a.real+b.real, a.image+b.image);//返回一个临时对象
}//作为 全局函数
int main()
{
complex a(4,4),b(1,1),c;
c=a+b;//a+b等价于c=operator+(a,b);
cout<<(a-b).real;//a-b等价于a.operator-(b)
(2)重载为成员函数时,参数个数为运算符目数(操作数)减一,重载为普通函数时,参数个数为运算符目数。
(3)赋值运算符重载=
- a=b的返回值是对a的引用,即&a。
- 只能重载于成员函数
(4)运算符重载为友元函数
2. 重载类型转换运算符
(六)输入输出、文件读写
1. 输入输出
2.流插入运算符和流提取运算符的重载
cout<<5<<”this”;//为什么能够成立?
本质上的函数调用形式是:cout.operator<<(5).operator<<(“this”)
cout是在iostream中定义的,是ostream类的对象,<<发生了重载
怎么重载cout<<5?
等价于cont.operator<<(5)
void ostream::operator<<(int n)
{……//输出n的代码
return;
}
怎么重载cout<<”this”?
等价于cout.operator<<(“this”);
ostream&ostream::operator<<(int n) //返回值为引用,还是返回cout
{……//输出n的代码
return *this;
}
2. 文件读写
文件读写示例程序:
(七)标准模板库STL
- 容器是一个与数组类似的单元,可以存储若干个值。
- 算法使完成特定任务(如对数组进行排序或在链表中查找特定值)的处方。
- 迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针。
- 函数对象是类似于函数的对象,可以是类对象或函数指针(包括函数名,因为函数名被用作指针)。
- STL使得能够构造各种容器(数组、队列、链表),以及执行各种操作(搜索、排序、随机排列)。