存储:存在哪?怎么存?什么时候释放?
使用:在哪用?怎么用?什么时候用?单独使用?交叉使用?
默认定义了什么,自定义;执行优先级,执行内容;相同区别,不同相似。
不同编译器……
浅入C++
类和对象
1. 类和对象的基本概念
- 封装
把变量(属性)和函数(操作)合成一个整体,封装在一个类中;对变量和函数进行访问控制;数据封装是一种把数据和操作数据的函数捆绑在一起的机制,数据抽象是一种仅向用户暴露接口而把具体的实现细节隐藏起来的机制。
C++程序 = 程序语句+程序数据
程序语句(代码):这是程序中执行动作的部分,它们被称为函数。
程序数据:数据是程序的信息,会受到程序函数的影响。//封装两层含义 //1. 属性和行为合成一个整体 //2. 访问控制,现实事物本身有些属性和行为是不对外开放 class Person{ //人具有的行为(函数) public: void Dese(){ cout << "我有钱,年轻,个子又高,就爱嘚瑟!" << endl;} //人的属性(变量) public: int mTall; //多高,可以让外人知道 protected: int mMoney; // 有多少钱,只能儿子孙子知道 private: int mAge; //年龄,不想让外人知道 }; int main(){ Person p; p.mTall = 220; //p.mMoney 保护成员外部无法访问 //p.mAge 私有成员外部无法访问 p.Dese(); return EXIT_SUCCESS; }
- 访问权限
1.在类的内部(作用域范围内),没有访问权限之分,所有成员可以相互访问
2.在类的外部(作用域范围外),访问权限才有意义:public,private,protected
3.在类的外部,只有public修饰的成员才能被访问,在没有涉及继承与派生时, private和protected是同等级的,外部不允许访问
4.子类访问,public 、private 可以protected不可以。
[struct和class的区别?]
C++中除默认权限不一样外其它一样。class默认访问权限为private,struct默认访问权限为public.
2. 面向对象程序设计思想
-
增插函数调用
#include "circle.h" void Circle1::setR(int r) { c_r = r; } int Circle1::getR() { return c_r; } void Circle1::setPoint(Point point) { c_point = point; } Point Circle1::getPoint() { return c_point; }
3. 构造和解析函数
-
两个函数理解产生、销亡。类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。
#include<iostream> #include<string> using namespace std; class Person { public: int age; string name; //系统内部函数执行顺序 存储位置 Person()//构造函数函数生成时执行:与类同名,没有返回值,可以无参,可以有参(重载) { cout << "构造 函数1" << endl; } Person(int a)//构造函数函数生成时执行:与类同名,没有返回值,可以无参,可以有参(重载) { cout << "构造 函数2" << endl; } ~Person()//析构函数函数结束时执行:~+类名,无参数、不重载 { cout << "析构函数" << endl; } }; void main() { Person p; p.~Person(); Person p2(1); Person p3; system("pause"); }
-
系统会默认产生构造函数(无参数)和析构函数,默认调用:生成时调用构造,结束时调用析构函数。
-
拷贝(构造)函数
#include<iostream> #include<string> using namespace std; class Person { public: int age; string name; Person(const Person &p) { //拷贝构造函数:拷贝内容::: this->age = p.age; this->name = "定义拷贝内容";//this 指向当前类的对象属性,下面会介绍;默认不加,但是 age 名字区分主要别名。 } Person() {} }; void test() { Person p;//定义 Person p()不可行,返回Person 类型函数。 p.age = 2; p.name = "kao bei"; cout << " p年龄: " << p.age << endl; Person pp(p); cout << " pp年龄: " << pp.age << endl; cout << " pp年龄: " << pp.name << endl; } void main() { test(); system("pause"); }
-
匿名对象 Person(10); Person p = Person(10);不能使用拷贝函数初始化匿名对象(拷贝函数左值),如果写成拷贝函数右值可以。
构造函数的分类及调用
1. 按照参数分类
无参构造(默认构造) 有参构造
2. 按照类型分类
普通构造函数 拷贝构造函数
3. 无参构造写法 和调用
Person p1 ; 注意不能写 Person p1() ,因为编译器认为这个是函数声明
4. 有参构造写法 和调用
Person p2(10) 或者 Person p2 = Person(10); Person(10) 匿名对象 ,执行当前行后就会释放这个对象。
5. 拷贝构造函数
Person( const Person & p ); Perons p1( p2) 或者 Person p1 = Person(p2);不能用拷贝构造函数初始化匿名对象。如果写成 Person (p1) 这种写法等价于 Person p1写到右值可以做拷贝构造函数 。
6. 隐式函数
Person P = 100 隐式类型转换 相当于调用 Person p = Person(100)
参考网络 -
防止隐式类型转换explicit (英 [ɪk’splɪsɪt; ek-] 美 [ɪk’splɪsɪt] ) 关键字,explicit用于修饰构造函数,防止隐式转化。是针对单参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造)而言
class MyString{ public: explicit MyString(int n){ cout << "MyString(int n)!" << endl; }//贝构造函数 Person( const Person & p ) MyString(const char* str){ cout << "MyString(const char* str)" << endl; } }; int main(){ //给字符串赋值?还是初始化? //Person P = 100 隐式类型转换 相当于调用 Person p = Person(100) //MyString str1 = 1; MyString str2(10); //寓意非常明确,给字符串赋值 MyString str3 = "abcd"; MyString str4("abcd"); return EXIT_SUCCESS; }
-
release(优化了) 和 debug 下调试结果可能不同,由于以值方式返回局部对象;1、用已经创建好的对象来初始化新的对象;2、以值传递的方式给函数参数传值;3、以值方式返回局部对象;4、release 默认下会做优化。
-
构造函数调用规则:系统默认提供默认构造函数、拷贝构造函数、析构函数,如果自己写了就不会有相应的默认函数了。
-
深拷贝、浅拷贝(值拷贝),堆区重复释放造成异常。同一类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是独立的两个对象,这种情况被称为浅拷贝。当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要自定义拷贝构造函数,自行给指针动态分配空间,深拷贝。
-
初始化列表。注意:初始化成员列表(参数列表)只能在构造函数使用。在构造函数后面 + : 属性(值、参数), 属性(值、参数)—>Person(int a, int b, int c) : mA(a), mB(b), mC© {}
class Person{ public: #if 0 //传统方式初始化 Person(int a,int b,int c){ mA = a; mB = b; mC = c; } #endif //初始化列表方式初始化 Person(int a, int b, int c):mA(a),mB(b),mC(c){} void PrintPerson(){ cout << "mA:" << mA << endl; cout << "mB:" << mB << endl; cout << "mC:" << mC << endl; } private: int mA; int mB; int mC; };
-
类对象作为成员的案例。当类对象作为类的成员时候,构造顺序是先构造类对象的构造,然后构造自己,析构顺序与构造相反(从存储记忆)。
-
new 和 delete:Person * p = new Person 会返回一个Person指针,默认调用构造函数,开辟空间,返回不是void* ,不需要强制转换;delete释放; new 对象 用void* 去接受,释放不了对象;new出来的是数组 ,如何释放? delete [] …;new出来的是数组,肯定会调用默认构造;new出来存放到堆区。
Person* person = new Person;//void *p = new Person;无法释放!! Person * personArr= new Person[10];//new 数组,调用默认构造函数(如果重新构造函数了,添加默认构造函数)。 delete [] personArr; Person pArr[2] = {Person(1),Person(2)};//堆区 Person* person = new Person; 相当于: Person* person = (Person*)malloc(sizeof(Person)); if(person == NULL){ return 0; } person->Init(); //melloc 返回void*(万能指针) 还需要强转,不会调用构造; delete person;
-
静态成员变量 static 关键字。在类定义中,它的成员(包括成员变量和成员函数),这些成员可以用关键字static声明为静态的,称为静态成员。静态成员就是一个only one,使用时想只有一个且共用。
- 静态成员变量:编译阶段分配内存、所有对象共享数据、通过对象访问、通过类名访问、有权限控制、类内声明 类外初始化 [ 加作用域(编译器认为还是类内)]。
- 静态成员函数;可以访问静态成员变量,不可以方法普通成员变量;普通成员函数 都可以访问;静态成员函数也有权限;可以通过对象访问,也可以通过类名进行访。
-
静态成员(单例表示),一个程序只能有一个main(),静态(only one)。目的 为了让类中只有一个实例,实例不需要自己释放;将 默认构造 和 拷贝构造 私有化;内部维护一个 对象指针;私有化唯一指针;对外提供 getInstance方法来访问这个指针;保证类中只能实例化唯一一个对象。
class CSingleton { public: static CSingleton* GetInstance() { if ( m_pInstance == NULL ) m_pInstance = new CSingleton(); return m_pInstance; } private: CSingleton(){}; static CSingleton * m_pInstance; };
4. 面向对象模型
-
封装,成员变量 和 成员操作分开存储,只有非静态成员变量属于类。
-
关键字this, this 指针。this指针指向被调用的成员函数所属的对象。普通成员函数默认增加(类名* this)this ,保存对象地址。
class Person{ public: //1. 当形参名和成员变量名一样时,this指针可用来区分 Person(string name,int age){ //name = name; //age = age; //输出错误 this->name = name; this->age = age; } //2. 返回对象本身的引用 //重载赋值操作符 //其实也是两个参数,其中隐藏了一个this指针 Person PersonPlusPerson(Person& person){ string newname = this->name + person.name; int newage = this->age + person.age; Person newperson(newname, newage); return newperson; } void ShowPerson(){ cout << "Name:" << name << " Age:" << age << endl; } public: string name; int age; }; //3. 成员函数和全局函数(Perosn对象相加) Person PersonPlusPerson(Person& p1,Person& p2){ string newname = p1.name + p2.name; int newage = p1.age + p2.age; Person newperson(newname,newage); return newperson; } //链式编程,年龄相加。 Person& personAge(Person & p) { this->age + = p.age; return *this;//*this指向对象本体 } int main(){ Person person("John",100); person.ShowPerson(); cout << "---------" << endl; Person person1("John",20); Person person2("001", 10); //1.全局函数实现两个对象相加 Person person3 = PersonPlusPerson(person1, person2); person1.ShowPerson(); person2.ShowPerson(); person3.ShowPerson(); //2. 成员函数实现两个对象相加 Person person4 = person1.PersonPlusPerson(person2); person4.ShowPerson(); system("pause"); return EXIT_SUCCESS; }
-
this指针使用:指针永远指向当前对象,解决命名冲突;*this 指向对象本体;非静态的成员函数才有this指针。
-
空指针访问成员函数Person *p = NULL:如果成员函数没有用到this,那么空指针可以直接访问;如果成员函数用的this指针,就要注意,可以加if判断,如果this为NULL就return。
-
const修饰函数体(常函数void func() const {} 常函数):修饰是this指针 const Type * const this,不能修改this指针执行的值;用mutable修饰的关键字是在常函数可以修改的; 常对象:在对象前 加入 const修饰 const Person p1 关键字,const使用小结参考:const修饰变量,修饰参数,修饰函数体,修饰对象。
5. 友元
-
关键字friend,全局函数做友元函数;全局函数写到 类中做声明 并且最前面写关键字 friend;让整个类 做友元类;friend class 类名 ;友元类 是单向,不可传递的;让成员函数做友元函数;friend void goodGay::visit();
参考传智播客+黑马的视频如果一个类被声明为friend,意味着它不是这个类的成员函数,却可以修改这个类的私有成员,而且必须列在类的定义中,因此他是一个特权函数。c++不是完全的面向对象语言,而只是一个混合产品。增加friend关键字只是用来解决一些实际问题,这也说明这种语言是不纯的。毕竟c++设计的目的是为了实用性,而不是追求理想的抽象。
— Thinking in C++class Building; //友元类 class MyFriend{ public: //友元成员函数 void LookAtBedRoom(Building& building); void PlayInBedRoom(Building& building); }; class Building{ //全局函数做友元函数 friend void CleanBedRoom(Building& building); #if 0 //成员函数做友元函数 friend void MyFriend::LookAtBedRoom(Building& building); friend void MyFriend::PlayInBedRoom(Building& building); #else //友元类 friend class MyFriend; #endif public: Building(); public: string mSittingRoom; private: string mBedroom; }; void MyFriend::LookAtBedRoom(Building& building){ cout << "我的朋友参观" << building.mBedroom << endl; } void MyFriend::PlayInBedRoom(Building& building){ cout << "我的朋友玩耍在" << building.mBedroom << endl; } //友元全局函数 void CleanBedRoom(Building& building){ cout << "友元全局函数访问" << building.mBedroom << endl; } Building::Building(){ this->mSittingRoom = "客厅"; this->mBedroom = "卧室"; } int main(){ Building building; MyFriend myfriend; CleanBedRoom(building); myfriend.LookAtBedRoom(building); myfriend.PlayInBedRoom(building); system("pause"); return EXIT_SUCCESS; }