1.explicit关键字
explicit关键字 修饰构造函数 作用是不能通过隐式法调用构造函数。
首先我们先了解构造函数的隐式转换:
#include<iostream> using namespace std; class mystring { public: mystring(int n) { cout << "mysting(const char *str)" << endl; } mystring(const char* str) { cout << "mysting(conts char *str)"<<endl; } }; int main() { //给字符串赋值?还是初始化? mystring st1=1;//隐式构造函数 //因为实例化对象要调动构造函数,而这里就会寻找构造函数其参数为整形的,而不是赋值 //所以这是初始化 mystring str2(10);//构造函数赋值,这里看比较显而易见 //寓意非常明显,给字符串赋值 mystring st3 = "abcd"; mystring str4 = ("abcd"); return 0; }
我们发现有时候隐式的构造函数赋值时写法会与赋值这两种混淆含义,不清楚该操作是初始化构造函数还是给对象赋值,于是我们使用了一个关键字explicit,用来修饰构造函数不会有初始化是以上代码“mystring str2=1”这种方式,这种方式只能是给认为给对象赋值。
explicit mystring(int n) { cout << "mysting(const char *str)" << endl; }
这样修饰构造函数之后,在这样写是无法找到对印的构造函数:
类的对象数组
本质上是一个数组,其成员为类的对象。
举例:
class A { public: int a;//这里我们为了便于访问就直接定义为公有的 public: A() { a = 0; cout << "A的无参构造a=" << a << endl; } A(int x) { a = x; cout << "A的有参构造a=" << a << endl; } ~A() { cout << "A的析构函数 a=" << a << endl; } }; int main() { //对象数组 每个元素都会自动调用构造函数和析构函数 //对象数组不初始化 每个元素是调用无参构造的 A arr1[5]; //对象初始化时,必须显示使用有参构造 逐个元素初始化 A arr2[5] = { A(10),A(20),A(30),A(40),A(50) }; int n = sizeof(arr2) / sizeof(arr2[0]); int i = 0; for (i = 0; i < n; i++) { cout << arr2[i].a<<" " ; } cout << endl; }
在这里,数组元素即类的对象的创建是遵循栈的规则,先创建后释放。
我们可以观察构造函数调用与释放时的顺序。创建时先从前往后创建,释放时从后往前释放。
动态对象的创建与初始化
1.动态创建的概述
当我们创建数组的时候,总需要提前预定数组长度,然后编译器分配预定长度的数组空间。但我们学了c语言就知道,在不知道的数组大小前提下,给定空间可能太大,浪费空间,可能太小,空间不够用。而动态创建专门解决这类问题,在c语言中提供专门开辟空间的函数malloc realloc,calloc结合释放空间的函数free是现在堆区上动态开辟空间,需要多少,开辟多少。
2.c语言方式创建动态对象
当创建一个c++对象时会发生两件事:
1.为对象分配内存
2.调用构造函数初始化那片内存空间。
而第二步时确保一定能发生的。c++强迫我们必须要这样做是因为使用未初始化的对象是程序报错的重要原因。在c语言中提供专门开辟空间的函数malloc realloc,calloc,这些函数式时能有效开辟空间的,但是原始的,需要我们小心谨慎的使用。
举例:利用c动态开辟的的方式
class person { public: person() { cout << "person无参构造" << endl; } ~person() { cout << "person析构" << endl; } int a; }; void test01() { person * p = (person*)malloc(sizeof(person)); //判断是否开辟成功 //调用初始化函数 //清理对象 free(p); } int main() { test01(); return 0; }
注意:malloc和free动态申请对象和释放对象 使用malloc和free函数去动态申请对象,和释放申请的对象时不会调用构造函数和析构函数。但是我们同样我们需要自己去调用初始化函数和清理对象的函数。
我们可以看到这是对于c++来说是,反其道而行之,我们必须自己去初始化,并清理。
出现的问题有:
1.程序员必须确定对象长度
2.malloc时返回一个void的指针,在c++中是不允许将void型的赋值给其他指针
3.malloc可能会申请内存失败
4.用户在使用对象前必须亲自初始化,之后亲自清理。
c的动态申请太复杂,我们在c++中不适用。
c++对象的动态申请
1.new创建动态对象
c++中解决动态分配的方案是把创建的一个对象所需的操作都结合在一个称为new的运算符中,当用new创建一个对象时,它就在堆区为对象分配所需的空间并完成初始化。如下:
person *person=new person;
new操作符能确定再调用构造函数初始化前内存分配时成功的,所以不用显示是否调用成功。因此在队里创建对象的过程变得更简单了。
2.delete释放动态对象
new的对头时delete,delete先调用析构函数,然后释放内存。
举例:
class person { public: person() { cout<<"无参构造函数!"<<endl; pname = new char[strlen("undefined") + 1]; strcpy(pname, "undefined"); page = 0; } person( char *arr,int x) { cout << "有参构造函数!" << endl; pname = new char[strlen(arr) + 1]; strcpy(pname, arr); page = x; } void showperson() { cout << "name:" << pname << "age:" << page << endl; } ~person() { cout << "调用了析构函数" << endl; if (pname != NULL) { delete[]pname; pname = NULL; } } //这里为了方便调用还是用公共型的 public: char* pname; int page; }; int main() { person* person1 = new person;//自动调用无参构造 person* person2 = new person((char *)"john", 33);//自动调用有参构造 person1->showperson(); person2 -> showperson(); delete person1; delete person2; }
动态对象数组
所创建的的对象数组为动态开辟,举例
class person { public: person() { pname = NULL; page = 0; } person(char* name, int age) { pname = new char[strlen(name) + 1]; strcpy(pname, name); page = age; } ~person() { if (pname != NULL) { delete []pname; pname = NULL; } } public: char* pname; int page; }; void test() { //栈聚合初始化 person sperson[] = { person((char*)"john",12),person((char*)"marke",14) }; cout << sperson[1].pname << endl; //创建堆上的数组对象必须提供构造函数 person* workers = new person[20]; delete []workers; }
可以看到new与delete 是成对出现的,注意他们的使用方法:
类型 *p = new 类型;
delete p; 申请数组:
类型 *p = new 类型[n];
delete [ ]p;
静态成员
在类定义中,它的成员(包括成员变量和成员函数),这些成员可以用 关键字static声明为静态的,称为静态成员。 不管这个类创建了多少个对象,静态成员只有一个拷贝,这个拷贝被所 有属于这个类的对象共享。
静态成员变量
static修饰的静态成员是属于类的,而不是对象
静态成员变量在内存中只有一份
多个对象共享一个静态变量 静态成员变量
必须类内声明,类外定义 静态成员变量可以通过类的作用域访问
静态成员变量可以通过类的对象访问
static 修饰的成员,定义类的时候,必须分配空间(在编译阶段)。
举例:
class person { public: int a;//普通变量 //静态成员变量不能再类内初始化 类内只能声明,定义在全局 声明的作用只是限制静态 //成员变量作用域 static int b;//静态成员变量 在编译阶段就分配内存 存在静态全局区 }; int person::b = 10;//类中成员变量的定义 void test01() { person p1; p1.b = 100; cout << p1.b << endl; } void test02() { cout << person::b << endl;//通过类的作用域访问类的静态成员函数,无需再今经过对象 //cout << person::a << endl; } using namespace std; int main() { test01(); test02(); return 0; }
可以看到静态修饰后,它可以通过外部修改。
静态成员函数
跟静态成员变量一样,是直属于类的,直接通过类访问问。
.静态成员函数能访问静态成员变量不能访问普通的成员变量
.可以通过类的作用域访问静态成员函数
.可以通过对象访问静态成员函数
与静态变量不一样,变量在类之前就已经定义好了(外部定义)。但函数可以直接在内部定义。
举例:
class person { public: int a; //静态成员变量不能再类内初始化 类内只能声明,定义在全局 声明的作用只是限制静态 //成员变量作用域 static int b;//静态成员变量 在编译阶段就分配内存 存在静态全局区 void show() { cout << a << " " << b << endl;; } static void static_show()//静态成员函数 可以访问静态成员变量 不能访问普通的 //成员变量 { cout << " " << b << endl;; } }; int person::b = 100; void test01() { person::static_show();//通过流类的作用域访问静态成员函数 person p1; p1.static_show();//通过对象访问静态成员函数 } int main() { test01(); return 0; }
值得注意的是,静态成员函数只能操作静态成员变量,对普通的变量的无法操作。