原文链接:https://blog.csdn.net/qq_33670157/article/details/104455787
原文写的不够好,我已经进行总结优化。要看懂本文,需要有C语言,c++基础。一文有点长,打算分多篇来解释
分为c++基础,c++文件流相关的操作,网络编程。
一、C++和C
C语言是结构化和模块化的语言,面向过程。
C++保留了C语言原有的所有优点,增加了面向对象的机制,俗称“带类的C",1983年更名为C++
运算符
算术运算符: + - * / % ++ --
关系运算符: == != < > >= <=
逻辑运算符: && || !
位运算符: & | ^ ~ << >>
赋值运算符: = += -= *= /= %= <<= >>= &= ^= !=
一、面向对象,类
类也是一种数据类型。
类的声明:
class 类名
{
public:
公有数据成员;
公有成员函数;
private:
私有数据成员;
私有成员函数;
protected:
保护数据成员;
保护成员函数;
};
成员函数的定义:类内,类外,类外内联函数
//类外
返回类型 类名:成员函数名(参数列表)
{
函数体;
}
//内联函数:类外
inline 返回类型 类名:成员函数名(参数列表)
{
函数体;
}
内联函数的代码会直接嵌入到主调函数中,可以节省调用时间,如果成员函数在类内定义,自动为内联函数。
类成员的访问权限以及类的封装
和Java、C#不同的是,C++中public、private、protected只能修饰类的成员,不能修饰类,C++中的类没有共有私有之分
类内部没有访问权限的限制,都可以互相访问。
在C++中用class定义的类中,其成员的默认存取权限是private。
类(class)与结构体(struct)的区别
//结构体默认权限为public
struct person
{
void show();
string name;
int age;
};
int main()
{
person p;
p.name = "heiren";
p.age = 666;
p.show();
cout <<"name="<< p.name <<" age="<< p.age << endl;
system("pause");
return 0;
}
将struct改为class,运行报错。因为class不允许对象直接访问成员变量,只能通过成员函数访问。因为默认是private的。
二、对象
1.声明类同时定义对象
class 类名
{
类体;
}对象名列表;
2.先声明类,再定义对象
class 类名
{
类体;
}
类 对象
3. 不出现类名,直接定义对象
class
{
类体;
}对象名列表;
4.在堆上创建对象
Person p(123, "yar");//在栈上创建对象
Person *pp = new Person(234,"yar");//在堆上创建对象
注:不可以在定义类的同时对其数据成员进行初始化,因为类不是一个实体,不合法但是能编译运行
对象成员的引用方式:
对象名.数据成员名
或者 对象名.成员函数名(参数列表)
三、构造函数
是一种特殊的成员函数,主要功能是为对象分配存储空间,以及为类成员变量赋初值
构造函数名必须与类名相同
没有任何返回值和返回类型
创建对象自动调用,不需要用户来调用,且只掉用一次
类没有定义任何构造函数,编译系统会自动为这个类生成一个默认的无参构造函数
构造函数定义
1.类中定义
2.类中声明,类外定义
[类名::]构造函数名(参数列表)
{
函数体
}
1、带默认参数的构造函数
class Person
{
public:
Person(int = 0,string = "张三");//类中声明构造函数,带默认参数初始化
void show();
private:
int age;
string name;
};
Person::Person(int a, string s)//类外定义构造函数
{
cout<<a<<" "<<s<<endl;
age = a;
name = s;
}
void Person::show()
{
cout << "age="<<age << endl;
cout << "name=" <<name << endl;
}
int main()
{
Person p; //对象输出为,0 张三
Person p2(12); //对象输出为 ,12 张三
Person p3(123, "yar"); //对象输出为 ,123 yar
return 0;
}
2、带参数初始化表的构造函数
类名::构造函数名(参数列表):参数初始化表
{
函数体;
}
参数初始化列表的一般形式:
参数名1(初值1),参数名2(初值2),…,参数名n(初值n)
class Person
{
public:
Person(int = 0,string = "张三");
void show();
private:
int age;
string name;
};
Person::Person(int a, string s):age(a),name(s)//类外定义构造函数,初始化列表
{
cout << a << " " << s << endl;
}
构造函数重载:
构造函数名字相同,参数个数和参数类型不一样。
class Person
{
public:
Person();
Person(int = 0,string = "张三"); //两个构造函数 int
Person(double,string); //两个构造函数 double
void show();
private:
int age;
double height;
string name;
};
拷贝构造函数
类名::类名(类名&对象名)
{
函数体;
}
class Person
{
public:
Person(Person &p);//声明拷贝构造函数
Person(int = 0,string = "张三");
void show();
private:
int age;
string name;
};
Person::Person(Person &p)//定义拷贝构造函数
{
cout << "拷贝构造函数" << endl;
age = 0;
name = "ABC";
}
Person::Person(int a, string s):age(a),name(s)
{
cout << a << " " << s << endl;
}
int main()
{
Person p(123, "yar");
Person p2(p);
p2.show();
return 0;
}
// p输出结果
123 yar
//拷贝构造函数输出结果
age=0
name=ABC
五、析构函数
析构函数的定义:
1.类中定义
2.类中声明,类外定义
[类名::]~析构函数名()
{
函数体;
}
六、指针
1、指向对象的指针
对象指针的声明和使用
类名 *对象指针名;
对象指针 = &对象名;
访问对象成员方法
对象指针->数据成员名
对象指针->成员函数名(参数列表)
Person p1(123, "yar");
Person * Ptr1 = &p1;
Person * Ptr2 = new Person(234,"yar")
Ptr2 ->show();
2、指向对象成员的指针
数据成员类型 *指针变量名 = &对象名.数据成员名;
函数类型 (类名::*指针变量名)(参数列表);
指针变量名=&类名::成员函数名;
(对象名.*指针变量名)(参数列表);
Person p(123, "yar");
void(Person::*pfun)();
pfun = &Person::show;
(p.*pfun)();
解释
void show()是个void返回类型,返回值是void
void(Person:: * pfun)()是什么意思呢,拆开来分析
C语言里定义指针时是这样 int * ptr,ptr是int型的指针,C语言里定义函数指针时是(int * pfunc)()。pfunc指向一个函数,这个指针时int型。
所以void(Person::*pfun)()意思,就死pfun是个指针,指向这个函数,这个指针类型是person类,类型,这个函数返回值是void型。
(p.*pfun)()的意思,先解析解引用pfun,是show,(p.*pfun)()等同于Ptr2 ->show()
八、this指针
每个成员函数都有一个特殊的指针this,它始终指向当前被调用的成员函数操作的对象
其实类似于C语言的结构体,头指针,这个头,就是一段内存地址的首地址,有了头,就可以访问到所有地址。
struct person
{
struct person *ptr;
int a;
void show();
}
struct person a;
struct person * p
p = a;//a的地址给p,就是把这个结构体的首地址,指向了这个结构体,this就是这个意思
p->show();
p = a.ptr; //this就是这个意思
class Person
{
public:
Person(int = 0,string = "张三");
void show();
private:
int age;
string name;
};
Person::Person(int a, string s):age(a),name(s)
{
cout << a << " " << s << endl;
}
void Person::show()
{
cout << "age="<<this->age << endl;
cout << "name=" <<this->name << endl;
}
九、友元
借助友元(friend),可以使得其他类中得成员函数以及全局范围内得函数访问当前类得private成员。
我的理解,假装是他的内部成员函数,就可以访问类得private成员。骗过以前的规则,外部不能直接访问类内部的数据
我的理解,假装是他的内部成员函数,就可以访问类得private成员。骗过以前的规则,外部不能直接访问类内部的数据
我的理解,假装是他的内部成员函数,就可以访问类得private成员。骗过以前的规则,外部不能直接访问类内部的数据
其实发现偶尔需要这样做,所以强行打补丁,找借口
1、友元函数
友元函数不是类的成员函数,所以没有this指针,必须通过参数传递对象。
友元函数中不能直接引用对象成员的名字,只能通过形参传递进来的对象或对象指针来引用该对象的成员。
1.将非成员函数声明为友元函数
class Person
{
public:
Person(int = 0,string = "张三");
friend void show(Person *pper);//将show声明为友元函数,外部一个普通成员函数
private:
int age;
string name;
};
Person::Person(int a, string s):age(a),name(s)
{
cout << a << " " << s << endl;
}
void show(Person *pper)//非类。就是外部一个普通成员函数
{
cout << "age="<< pper->age << endl;
cout << "name=" << pper->name << endl;
}
int main()
{;
Person *pp = new Person(234,"yar");
show(pp);
system("pause");
return 0;
}
2.将其他类的成员函数声明为友元函数
person中的成员函数可以访问MobilePhone中的私有成员变量
class MobilePhone;//提前声明
//声明Person类
class Person
{
public:
Person(int = 0,string = "张三");
void show(MobilePhone *mp);
private:
int age;
string name;
};
Person::Person(int a, string s):age(a),name(s)//构造函数
{
cout << a << " " << s << endl;
}
void Person::show(MobilePhone *mp)//内部的类成员函数,访问到了,其他类的成员数据。
{
cout << mp->year << "年 " << mp->memory << "G " << mp->name << endl;
}
//声明MobilePhone类
class MobilePhone
{
public:
MobilePhone();
friend void Person::show(MobilePhone *mp);//将外部类成员函数,声明为友员,可以这样,他假装是他的内部成员函数,就可以访问成员了。
private:
int year;
int memory;
string name;
};
MobilePhone::MobilePhone()
{
year = 1;
memory = 4;
name = "iphone 6s";
}
int main()
{
Person *pp = new Person(234,"yar");
MobilePhone *mp = new MobilePhone;
pp->show(mp);//传一个对象指针,就可以访问了
system("pause");
return 0;
}
友元类
语法形式:friend [class] 友元类名
当一个类为另一个类的友元时,称这个类为友元类。 友元类的所有成员函数都是另一个类中的友元成员。
我的理解,就是C语言里的结构体套结构体封装,就可以访问任何数据了。
class HardDisk
{
public:
HardDisk();
friend class Computer;
private:
int capacity;
int speed;
string brand;
};
HardDisk::HardDisk():capacity(128),speed(0),brand("三星")
{
}
class Computer
{
public:
Computer(HardDisk hd);
void start();
private:
string userName;
string name;
int ram;
string cpu;
int osType;
HardDisk hardDisk;
};
Computer::Computer(HardDisk hd):userName("yar"),name("YAR-PC"),ram(16),cpu("i7-4710"),osType(64)
{
cout << "正在创建computer..." << endl;
this->hardDisk = hd;
this->hardDisk.speed = 5400;
cout << "硬盘转动...speed = " << this->hardDisk.speed << "转/分钟" << endl;
}
void Computer::start()
{
cout << hardDisk.brand << " " << hardDisk.capacity << "G" << hardDisk.speed << "转/分钟" << endl;
cout << "笔记本开始运行..." << endl;
}
int main()
{
HardDisk hd;
Computer cp(hd);
cp.start();
system("pause");
return 0;
}
十、继承和派生
1 、继承和派生概述
继承就是再一个已有类的基础上建立一个新类,已有的类称基类或父类,新建立的类称为派生类和子类;派生和继承是一个概念,角度不同而已,继承是儿子继承父亲的产业,派生是父亲把产业传承给儿子。
一个基类可以派生出多个派生类,一个派生类可以继承多个基类
这句话感觉很拗口,其实不然,比如举个例子
1、一个派生类可以继承多个基类,的理解例子
定义一个吃的行为类
吃
{
躺着吃,
站在吃,
坐着吃
}
定义一个走路的行为类
{
两只脚走路,
四只脚走路,
}
定义一个睡觉的行为类
{
平躺着睡,
侧躺着睡,
趴躺着睡
}
然后就可以多继承了,继承了上面所有的行为,就是上面说的,一个派生类可以继承多个基类
定义一个动物狗类
{
吃的行为类
走路的行为类
睡觉的行为类
}
2、一个基类可以派生出多个派生类,的理解例子
定义一个吃的行为类
吃
{
躺着吃,
站在吃,
坐着吃
}
定义一个走路的行为类
{
两只脚走路,
四只脚走路,
}
定义一个睡觉的行为类
{
平躺着睡,
侧躺着睡,
趴躺着睡
}
然后一个基类就可以派生很多类了,如下面的猪狗,羊,牛,都有吃的行为,都是吃派生出来的。
定义一个动物狗类
{
吃的行为类
走路的行为类
睡觉的行为类
}
定义一个动物牛类
{
吃的行为类
走路的行为类
睡觉的行为类
}
定义一个动物羊类
{
吃的行为类
走路的行为类
睡觉的行为类
}
派生类的声明:
class 派生类名:[继承方式] 基类名
{
派生类新增加的成员声明;
};
继承方式为可选项,默认为private,还有public,protected
总结,如果是受保护不可见的,什么方式继承,都不可随意访问(受保护或不可见),如果是公开的,只有公开继承可见,其他方式继承都是原来是怎么样就怎么样。
利用using关键字可以改变基类成员再派生类中的访问权限;using只能修改基类中public和protected成员的访问权限。
class Base
{
public:
void show();
protected:
int aa;
double dd;
};
void Base::show(){
}
class Person:public Base
{
public:
using Base::aa;//将基类的protected成员变成public
using Base::dd;//将基类的protected成员变成public
private:
using Base::show;//将基类的public成员变成private
string name;
};
int main()
{
Person *p = new Person();
p->aa = 12;
p->dd = 12.3;
p->show();//出错
delete p;
return 0;
}
在C++中,如果基类和派生类中含有同名的成员
在C++中,如果基类和派生类中含有同名的成员,那么在派生类中直接访问该成员时将优先使用派生类自己的成员,这被称为"成员屏蔽"(member shadowing)。如果需要访问基类中被屏蔽的同名成员,则必须使用基类的作用域解析运算符(::)。
class Base {
public:
void foo() {
// 基类的foo函数
}
int some_member;
};
class Derived : public Base {
public:
void foo() {
// 派生类的foo函数
}
int some_member;
};
int main() {
Derived d;
d.foo(); // 调用Derived::foo
d.Base::foo(); // 调用Base::foo
d.some_member = 10; // 调用Derived::some_member
d.Base::some_member = 20; // 调用Base::some_member
return 0;
}
派生类的构造函数和析构函数
class Base
{
public:
Base(int, double);
~Base();
private:
int aa;
double dd;
};
Base::Base(int a, double d) :aa(a), dd(d)
{
cout << "Base Class 构造函数!!!" << endl;
}
Base::~Base()
{
cout << "Base Class 析构函数!!!" << endl;
}
class Person:public Base
{
public:
Person(int,double,string);
~Person();
private:
string name;
};
Person::Person(int a,double d,string str):Base(a,d),name(str)// 构造函数定义,初始化
{
cout << "Person Class 构造函数!!!" << endl;
}
Person::~Person()
{
cout << "Person Class 析构函数!!!" << endl;
}
int main()
{
cout << "创建Person对象..." << endl;
Person *p = new Person(1,2,"yar");
cout << "删除Person对象...." << endl;
delete p;
system("pause");
return 0;
}
多继承
一个派生类同时继承多个基类的行为。
多继承容易让代码逻辑复杂、思路混乱,一直备受争议,中小型项目中较少使用,后来的 Java、C#、PHP 等干脆取消了多继承
多重继承派生类声明的一般形式:
class 派生类名:继承方式1 基类1,继承方式2 基类2
{
派生类主体;
};
多重继承派生类的构造函数:
派生类名(总参数列表):基类名1(基类参数列表1),基类名2(基类参数列表2),
子对象名1,...(参数列表)
{
构造函数体;
}`
二义性问题:多个基类中有同名成员,出现访问不唯一的问题。
十一、虚基类
c++引入虚基类使得派生类再继承间接共同基类时只保留一份同名成员。
虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class)。
怎么理解呢,如上面举例说的
假如定义一个动物类
{
狗,
牛
猪
}
这些动物都继承了吃的行为,那动物类就继承了三次,这个类就有三个相同的吃的函数,显然是不对的。
虚基类的声明:class 派生类名:virtual 继承方式 基类名
class A//虚基类
{
protected:
int a;
};
class B: virtual public A
{
protected:
int b;
};
class C:virtual public A
{
protected:
int c;
};
class D:public B,public C
{
protected:
int d;
void show()
{
b = 123;
c = 23;
a = 1;
}
};
派生类的 同名成员 比虚基类的 优先级更高
十二、多态和虚函数
多态
不同的对象可以使用同一个函数名调用不同内容的函数。
虚函数
实现程序多态性的一个重要手段,使用基类对象指针访问派生类对象的同名函数。
这玩意有点像C语言的函数指针,回掉函数
class A
{
public:
virtual void show()
{
cout << "A show" << endl;
}
};
class B: public A
{
public:
void show()
{
cout << "B show" << endl;
}
};
int main()
{
B b;
b.show();//B show
A *pA = &b;
pA->show();//B show 如果show方法前没用virtual声明为虚函数,这里会输出A show
system("pause");
return 0;
}
纯虚函数
在基类中不执行具体的操作,只为派生类提供统一结构的虚函数,将其声明为虚函数。
class A
{
public:
virtual void show() = 0;
};
class B: public A
{
public:
void show()
{
cout << "B show" << endl;
}
};
抽象类:包含纯虚函数的类称为抽象类。由于纯虚函数不能被调用,所以不能利用抽象类创建对象,又称抽象基类。
十三、运算符重载
所谓重载,就是赋予新的含义。函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作。运算符重载(Operator Overloading)也是一个道理,同一个运算符可以有不同的功能。
函数类型 operator运算符(参数列表)
{
函数体
}
//举个栗子:定义一个向量类,通过运算符重载,可以用+进行运算。
class Vector3
{
public:
Vector3();
Vector3(double x,double y,double z);
public:
Vector3 operator+(const Vector3 &A)const;
void display()const;
private:
double m_x;
double m_y;
double m_z;
};
Vector3::Vector3() :m_x(0.0), m_y(0.0), m_z(0.0) {}
Vector3::Vector3(double x, double y,double z) : m_x(x), m_y(y), m_z(z) {}
//运算符重载
Vector3 Vector3::operator+(const Vector3 &A) const
{
Vector3 B;
B.m_x = this->m_x + A.m_x;
B.m_y = this->m_y + A.m_y;
B.m_z = this->m_z + A.m_z;
return B;
}
void Vector3::display()const
{
cout<<"(" << m_x << "," << m_y << "," << m_z << ")" << endl;
}
class Vector3
{
public:
Vector3();
Vector3(double x,double y,double z);
public:
Vector3 operator+(const Vector3 &A)const;
Vector3 operator++();
friend Vector3 operator-(const Vector3 &v1, const Vector3 &v2);
friend Vector3 operator--(Vector3 &v);
void display()const;
private:
double m_x;
double m_y;
double m_z;
};
Vector3::Vector3() :m_x(0.0), m_y(0.0), m_z(0.0) {}
Vector3::Vector3(double x, double y,double z) : m_x(x), m_y(y), m_z(z) {}
//运算符重载
Vector3 Vector3::operator+(const Vector3 &A) const
{
Vector3 B;
B.m_x = this->m_x + A.m_x;
B.m_y = this->m_y + A.m_y;
B.m_z = this->m_z + A.m_z;
return B;
}
Vector3 Vector3::operator++()
{
this->m_x ++;
this->m_y ++;
this->m_z ++;
return *this;
}
void Vector3::display()const
{
cout<<"(" << m_x << "," << m_y << "," << m_z << ")" << endl;
}
Vector3 operator-(const Vector3 &v1,const Vector3 &v2)
{
Vector3 B(v1.m_x - v2.m_x, v1.m_y - v2.m_y, v1.m_z - v2.m_z);
return B;
}
Vector3 operator--( Vector3 &v)
{
v.m_x--;
v.m_y--;
v.m_z --;
return v;
}
int main()
{
Vector3 v1(1, 2, 3);
Vector3 v2(2, 3, 2);
++v1;//v1.operator++(); 作为类成员函数可以显式调用
v1.display();
--v2;
v2.display();
Vector3 v3 = v1 + v2;// v1.operator+(v2);作为类成员函数可以显式调用
v3.display();
Vector3 v4 = v1 - v2;
v4.display();
return 0;
}
输入\输出运算符重载
friend ostream &operator<<( ostream &output, const Vector3 &v )
{
output << "F : " <<v.m_x<< " I : " << v.m_y<<v.m_z;
return output;
}
friend istream &operator>>( istream &input, Vector3 &v )
{
input >> v.m_x>> v.m_y>>v.m_z;
return input;
}
operator 类型名()
{
转换语句;
}
class Vector3
{
public:
Vector3();
Vector3(double x,double y,double z);
public:
Vector3 operator+(const Vector3 &A)const;
Vector3 operator++();
friend Vector3 operator-(const Vector3 &v1, const Vector3 &v2);
friend Vector3 operator--(Vector3 &v,int);
operator double()
{
return m_x + m_y + m_z;
}
void display()const;
private:
double m_x;
double m_y;
double m_z;
};
int main()
{
Vector3 v1(1, 2, 3);
double d = v1;
cout << d << endl;//6
return 0;
}