c++语法知识
(一)宏
1.常量宏
作用:消除神仙数
#define PI 3.14
2.函数宏
作用:小函数频繁调用
#define ADD(a,b) a+b
3.控制宏:开关
(1)“被重复引用”
是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的。比如:存在a.h文件#include "c.h"而此时b.cpp文件导入了#include “a.h” 和#include "c.h"此时就会造成c.h重复引用。
//在puu.h中有这样的定义
#ifndef PUU_H // 防止puu.h被重复引用
#define PUU_H
//coding……
#endif
(2)#pragma once
是一个比较常用的C/C++预处理指令,只要在头文件的最开始加入这条预处理指令,就能够保证头文件只被编译一次。但不是所有编译器都适用。和上一种方法差不多的意思。
4.补充
define中的三个特殊符号:#,##,#@
#define Conn(x,y) x##y
#define ToChar(x) #@x
#define ToString(x) #x
(二)库
dynamic: .a .dll
static : .lib
区别:参不参与源程序的生成,exe包不包含这个库
封装
封装:隐藏对象的属性和实现的细节,仅公开对外接口
1.c++的类
class student
{
private:
//attribute属性
int age;
char *name;
char *addr;
//基本的数据类型:数和串
//method action
public:
void init(char *a_name,int a_age);//方法的申明
int get_age();
protected://和继承有关
};
//构造方法
void Student::init(char *a_name,int a_age){
age=a_age;
name=a_name;
}
//方法的定义
int Student::get_age(){
return age;
}
数据成员是属性吗? 未必。“我作为一个人必须有flag吗?”
属性是数据成员吗? 是。
数据成员一定是类的组成部分吗? 未必。
2.类成员函数
成员函数可以定义在类定义内部(内联),或者单独使用范围解析运算符 :: 来定义。
class student
{
private:
int age;
char *name;
char *addr;
public:
int get_age(){
return age;
}
};
///或者
int Student::get_age(){
return age;
}
3.类的构造函数&析构函数
构造函数
的名称与类的名称是完全相同的,构造函数可用于为某些成员变量设置初始值。析构函数
是一种特殊的成员函数,它会在每次删除所创建的对象时执行。当对象超出它的作用域时(包含对象的右括号),编译器将自动调用析构函数。
class Test
{
int i;
int *j;
public:
Test(int a,int b);
Test(int a);
~Test();
}
Test::~Test(){
delete j;//析构时,指针会自动消亡;指针所指内存需显式释放。
}
Test::Test(){
this->j=NULL;
}
//构造函数:可能的多个
Test::Test(int a,int b){
this->i=a;
this->j=new int(b);//j是指针,指向内存
}
void main(){
Test t(1,2);//显式传递参数,到括号自动消亡
Test *p=new Test(1,2);//p局部变量,作用域结束消亡
delete p;//目的是调用析构,消亡p指向的空间。删除p指的j,j指的new int
}
判断题:
构造有new(分配空间)一定有析构–>>对
构造没有new一定没有析构–>>错,不代表其他没有new
还有可能是这个情况,注意delete了什么。
class Test
{
int i;
int *j;
public:
Test(int a);
~Test();
}
Test::~Test(){
delete j;
}
//对象的消亡一定会执行~Test,而这没有被初始化
Test::Test(int a){
this->i=a;
this->j=NULL;//没有这个是错的,否则会delete野指针
}
还可以使用初始化列表来初始化字段
Test::Test(int a,int* b):i(a),j(b){
}
构造和析构别的知识
①构造函数的初始值有时必不可少
如果成员是const、引用,或者属于某种为提供默认构造函数的类类型,我们必须通过构造函数初始值列表为这些成员提供初值。
class ConstRef{
public:
ConstRef(int ii);
private:
int i;
const int ci;
int &ri;
};
//错误的构造函数
ConstRef::ConstRef(int ii){
i=ii;
ci=ii;
ri=i;
}
//正确的构造函数
ConstRef::ConstRef(int ii):i(ii),ci(ii),ri(i){
}
int main(int argc,char*argv[]){
ConstRef t(1);
}
错误构造函数的报错
②成员初始化的顺序
如果一个成员使用另一个成员来初始化,顺序就很关键了。
初始化是按照成员声明的顺序,所以需要保证顺序一致。
class X{
int i;
int j;
public:
//错误,但是我的编译器没报错
X(int val):j(val),i(j){}
};
4.引用和指针
①区别
- 不存在空引用。引用必须连接到一块合法的内存。
- 一旦引用被初始化为一个对象,就不能被指向到另一个对象。指针可以在任何时候指向到另一个对象。(引用是对象的别名)
- 引用必须在创建时被初始化;指针可以在任何时间被初始化。(这一点也很重要,相当于引用就是为了使用一个对象而生的。)e.g. int x;int& a=x;
总的来说引用和指针在用法上很像,都是去访问对象本身。但是由于很多人觉得Pointer is ugly!
就把指针暴露地址的做法隐藏起来了,使得引用也能直接访问对象本身。
②函数的引用
引用的使用经常发生在函数参数和返回值中。
如果引用加了const,就是input参数(只读);不加就是output参数(可写)。
一个简单的例子
void fun(int& i){
i++; //长的貌似就是个值
}
int main(int argc,char*argv[]){
int m =10;
cout << m << endl;
fun(m);//传递的是个引用 “引用就是披着羊皮的狼” “借值之名行指针之时”
cout << m << endl;
}
常量引用–引用需要在创建时被初始化
void f(int& i){}
void g(const int& i){}//可以编译,但这个值不能被改变
int main(){
//! f(1)
g(1);
}
指针引用–引用的对象是一个指针
void increment(int*& i ){i++;}
int main(){
int *i=0;
cout<<i<<endl;
increment(i);
cout<<i<<endl;
}
结果:
0
0x4
③拷贝构造函数
对一个已知对象进行拷贝,编译系统会自动——拷贝构造函数,如果用户未定义拷贝构造函数,则会调用默认拷贝构造函数。
默认拷贝构造函数--浅拷贝(位拷贝)
:如果对象成员中有指针的话,这两个指针将会指向同一个内存空间。在程序结束时,调用析构函数,这块内存会被释放两次,造成内存泄漏!(第二个指针所指的内存被释放变成了野指针)典型的浅拷贝还有memcpy()。
自定义拷贝构造函数--深拷贝(逻辑拷贝)
:在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间。
class Test
{
int i;
int *j;
public:
Test(int a,int b);
Test(Test& t);
~Test();
};
Test::Test(Test &t){
this->i=t.i;
this->j=new int(*t.j);
}
Test::Test(int a,int b){
this->i=a;
this->j=new int(b);//j是指针,指向内存
}
Test::~Test(){
delete j;
}
int main(){
Test t1(1,2);
Test t2(t1);
}
析构的时候t2先死,t1后死。
5.static
- static local variable:保值,main结束才释放
- static global function:仅本文件调用
- static data member:共享内存
跨文件调用可以- extern int global;
- extern void fun1();
不能把全局变量放进头文件,因为有且一次定义。
static变量在第一次使用时被创建,结束时释放
(全局变量在程序开始之前创建)
void fun(){
Test t1;//创建十次死亡十次
static Test t2;//创建一次死亡一次
}
静态变量在静态区,全局变量在静态区
全局变量和静态变量放在一起。
int main(int argc, char* argv[]){
int i;
int j;
static int m;
cout << &i << endl;
cout << &j << endl;
//i和j的地址差4,m不和它们在一起
cout << &m << endl;
return 0;
}
程序间通信的方法
- 函数调用
- function member,data member
- t1,t2 类中有static类型的变量
- 4.static function member,函数成员
static函数
- 与具体对象相关操作无关;
- 操作static成员。
在 C++ 中,static 静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体的外部,通常在类的实现文件中初始化,如:
double Account::Rate = 2.25;
static 关键字只能用于类定义体内部的声明中,定义时不能标示为 static。
Time t;
t.GetCurrentTime();//不再依赖具体对象
//也可以直接通过类名调用
Time::GetCurrentTime();
class Test{
public:
static int i;//属于本类全部对象。
Test();
static void a_fun();
};
int Test::i=10;//类的static int初始化
Test::Test(){
cout <<"building"<<endl;
}
void Test::a_fun()
{
//! this->i=10;error 调用者对象,static没对象
i=20;
}
int main(){
Test t;
cout<<t.i<<endl;
//等价
t.a_fun();
Test::a_fun();
cout<<t.i<<endl;
}
这里还需要注意一个问题:static const可以是private初始化。像static int i;是public的并且最好在类外(int Test::i=10)被初始化。
6.const
const 成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须有构造函数。
常常在运算符重载的时候使用。
operator type() const;
用const修饰方法内函数时,要求该方法不得修改成员属性。
void get_1() const{ return i;}
如果定义了常量对象,只能调用类里的常量方法。
class Test{
int i;
public:
void set_i(int ai){
i=ai;//const 不能修饰这个方法
}
void get_1() const{ //const:本函数允许常量,前提方法内不允许改变属性
return i;
}
}
int main(char*[] argv,int argc){
const Test t;//常量对象,不允许非常量方法
t.get_i();//报错--》因为get_i极有可能修改数据
return 0;
}
7.运算符重载
- 不是必须的
- 重载后要和人的基础认知相同,允许+不允许-
class Account{
char* name;
int id;
//....
int balance;
public:
Account();
void Save(int money);
Account operator+(int money);
};
Account Account::operator+(int money){
this->balance += money;
return *this;
}
Account::Account(){
balance=0;
}
void Account::Save(int money){
this->balance += money;
}
int main(){
Account a;
a.Save(100);
a=a+100;//把+理解成一个函数
}
8.new/delete vs. malloc/free
- new/delete是C++关键字,需要编译器支持。malloc/free是库函数,需要头文件支持。
- 使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算。而malloc则需要显式地指出所需内存的尺寸。
- new,返回的是对象类型的指针;malloc返回的是void * ,需要通过强制类型转换。
- malloc/free只能动态的申请和释放内存,无法强制要求其做自定义类型对象构造和析构工作。
class Memory{
public:
//一个构造
//一个析构
}
int main(char*[] argv,int argc){
//new = malloc + constructor
Memory *p = new Menory();
Memory *p =(Memory*)malloc(sizeof(Memory));
//delete = 析构 + free
}
9.friend
类可以允许其他类或者函数访问它的非公有成员。
friend关键词,类把一个函数作为它的友元。
class Box
{
double width;
public:
friend void printWidth( Box box );
void setWidth( double wid );
};
// 成员函数定义
void Box::setWidth( double wid )
{
width = wid;
}
// 请注意:printWidth() 不是任何类的成员函数
void printWidth( Box box )
{
/* 因为 printWidth() 是 Box 的友元,它可以直接访问该类的任何成员 */
cout << "Width of box : " << box.width <<endl;
}
// 程序的主函数
int main( )
{
Box box;
// 使用成员函数设置宽度
box.setWidth(10.0);
// 使用友元函数输出宽度
printWidth( box );
return 0;
}
声明类 ClassTwo 的所有成员函数作为类 ClassOne 的友元,需要在类 ClassOne 的定义中放置如下声明:
friend class ClassTwo;
10.this指针
在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。
//成员函数
int compare(Box box)
{
return this->Volume() > box.Volume();
}
//调用的时候
int rt=Box1.compare(Box2);
继承
继承描述的是共性与特性。
1.reuse
共性–自底向上重写。
个性–子类可以定义父类没定义的。
java有且仅有一个父类,cpp可以有多个或者无父类。
class Borrower{
int id;
char *name;
public:
void borrow();
void returnbook();
};
void Borrower::borrow(){
cout << "刷卡" << endl;
}
class Teacher : public Borrower{//继承语法
//……
//……
public:
//……
}
void Teacher::borrow(){
Borrower::borrower();//共性的体现
cout << "借5本" << endl;
}
class Student{
int id;
char *name;
public:
void borrow();
}
如果父类前没有public
,变成了class Teacher : Borrower
相当于class Cat : private Pet
导致了父类的公有,子类变成了私有,使得人为削弱继承。(private的存在仅仅是维护语法完整性)
2.protected
父类的私有子类不能用,父类的public和protected可以用。
class Pet{
protected:
int age;
char *name;
public:
void Speak()
{
cout << "speak" << endl;
}
};
class Cat:public Pet{
public:
void fun(){
this->age=2;//父类的私有子类不能用,protected可以用
}
void Speak()//redifination
{
Pet::Speak();、
cout << "miao" << endl;
}
}
3.组合
变相继承,通过子类创建一个父类对象,调用父类的东西。
class Engine
{
public:
void run(){
cout << "engine is running " << endl;
}
};
class Car
{
Engine e;
int other;
public:
void run(){
e.run();//reuse 子类和父类很可能是相同的,这个不是必须要有关系
cout << "other run" <<endl;
}
}
int main(){
Cat c;
c.Speak();
return 0;
}
4.构造顺序
- 每创造一个对象,会首先调用父类构造;消亡时会先析构子类,后析构父类。
- 如果是上面组合的方式,也会先构造父类,再构造子类。
class Pet{
protected:
public:
Pet(){
cout << "Constructor Pet"<<endl;
}
};
class Cat:public Pet{
int type;
public:
Cat(){
cout << "Constructor Cat"<<endl;
}
};
int main(){
Cat c;
return 0;
}
5.子类构造方式
- 如果子类只有默认构造,父类没有默认构造会报错。
- 子类可以调用父类构造,也可以再写新的构造。
//父类构造
class Pet{
protected:
int age;
char *name;
Pet(int aage,char *aname){
this->age=aage;
this->name=aname;
cout << "Construct Pet"<<endl;
}
}
class Cat:public Pet{
int type;
public:
//子类构造1
Cat() : Pet(2,"dudu")//显示的说明怎么构造
{
cout << "Construct Cat"<<endl;
}
//子类构造2
Cat(int aage,char *aname,int atype):Pet(aage,aname),type(atype){
// this->type=atype;
cout << "Construct Cat"<<endl;
}
}
6.多重继承的同名函数问题
如果两个父类有同名函数,子类的写法需要变化。
class Base1
{
public:
void f(){}
};
class Base2
{
public:
void h(){}
void f(){}
};
class Derived : public Base1
{
Base2 b2;
public:
void f(){
b2.f();
}
}
老师说:“在你成为一个高手之前不要用多重继承,成为一个高手以后没有必要用多重继承。”
多态
1.基本语法
关键词:virtual
虚函数–>多态
- virtual 有一块额外开辟的空间;每一个虚函数的类都一个虚函数表(v-table);
每个对象有一个虚指针(v-ptr),在最前面的4字节,指向虚函数表。多个同类对象的指针是相同的。所以可以通过改虚指针,改里面的东西。
- 虚指针在构造函数时隐式初始化
- 传值就是传拷贝,会拷贝构造。传一个cat,拷贝构造成pet
- never pass by value
- 拷贝构造会破坏多态
- 子类的相应函数会自动继承virtual关键词,习惯显式写上。
class Pet{
protected:
int age;
char *name;
public:
virtual void Speak()
{
cout << "speak" << endl;
}
};
多态有损性能–>空间–>每个对象多一个指针
2.多态的场景upcasting
- binding : 绑定,将函数的一次调用,与函数入口相对应的
- early binding:浅绑定,函数调用之前就决定了执行什么
- later binding ,runtime binding ,dynamic binding:都一样,都指多态性
void Needle(Pet& pet)
{
pet.Speak();
}
int main(int argc,char*argv[]){
//upcasting 向上类型转换
cout << sizeof(Pet) << endl;
Cat cat;
Needle(cat);//多态的使用
return 0;
}
3.抽象基类
有纯虚函数的类是抽象基类,抽象基类不能被实例化,负责定义接口。
抽象类可以有数据成员,纯虚函数可以有函数体。
作用:
- 是父类,可以当参数传递。不能传值,因为不能实例化,没有对象。
- 纲领,子类中必须实现在父类中已有的方法。如果不重写父类中的纯虚函数,这个子类自动继承父类的纯虚函数,那么它还是抽象类,还是不能实例化。不能创建对象。
class Pet{//abstract class抽象类
protected:
int age;
char *name;
public:
virtual void Speak()=0;//pure virtual纯虚函数
};
4.接口
应用抽象类,串联本不相关,却有行为上共性的类。
class FlyObject {
public:
virtual void fly() = 0;
};
class Airplane : public Machine, public FlyObject {
};
class Bird : public Animal, public FlyObject {
};
5.析构
构造函数不加virtual/static,通过类名直接访问,与对象无关
析构往往要加virtual(是多态的)
class MyClass{
public:
MyClass();
virtual ~MyClass();
}
template
1.简单的例子
template <class T>//模板类
class Stack{
T pool[100];
int top;
public:
Stack():top(0){}//初始化!!!!
void Push(T i){
//isfull
pool[top++]=i;
}
T Pop(){
return pool[--top];//后++前--
}
}
//容器最适用于模板类型
int main (int argc,char*argv[]){
Stack<int> s;
for(int i=0;i<10;i++){
s.Push(i);
}
for(int i=0;i<10;i++){
cout << pool[i] << endl;
}
}
2.STL
#include <vector>
#include <list>
using namespace std;
//1.万能容器
//2.动态增长
//工程不考虑性能问题
class Test{
int a[10000];
public:
Test(const Test&T){
static int cnt=0;
cout<<cnt<<endl;
}
}
int main (int argc,char*argv[]){
// vector<int> vi;
// list<int> vi;
list<Test> vi;
vector<Test> vi;
//vector 是先用,不够了找一块更大的搬家
//list 来一块分一块
for(int i=0;i<10000;i++){
Test t;
vi.push_back(t);
}
//iterator迭代器
vector<int>::iterator it = vi.begin;
while(it!=vi.end()){
cout<<*it<<Endl;
it++;
}
}
设计模式
1.单例模式
class Singleton{
public:
static Singleton* getInstance();
private:
Singleton();
//把复制构造函数和=操作符也设为私有,防止被复制
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
static Singleton* instance;
};
Singleton::Singleton(){}
Singleton::Singleton(const Singleton&){}
Singleton& Singleton::operator=(const Singleton&){}
//在此处初始化
Singleton* Singleton::instance = new Singleton();
Singleton* Singleton::getInstance(){
return instance;
}
int main(){
Singleton* singleton1 = Singleton::getInstance();
Singleton* singleton2 = Singleton::getInstance();
if (singleton1 == singleton2)
fprintf(stderr,"singleton1 = singleton2\n");
return 0;
}
2.工厂模式
AbstractProduct父类:
class AbstractProduct{
public:
AbstractProduct();
virtual ~AbstractProduct();
public:
virtual void operation() = 0;
};
两种产品的子类
class ProductA:public AbstractProduct{
public:
ProductA();
virtual ~ProductA();
public:
void operation();
};
class ProductB:public AbstractProduct{
public:
ProductB();
virtual ~ProductB();
public:
void operation();
};
AbstractFactory类
class AbstractFactory{
public:
AbstractFactory();
virtual ~AbstractFactory();
public:
//重点,返回的是AbstractProduct*
virtual AbstractProduct* createProduct(int type) = 0;
};
class SimpleFactory:public AbstractFactory{
public:
SimpleFactory();
~SimpleFactory();
public:
AbstractProduct* createProduct(int type);
};
main:
int main(){
AbstractFactory* factory = new SimpleFactory();
AbstractProduct* product = factory->createProduct(1);
product->operation();
delete product;
product = NULL;
product = factory->createProduct(2);
product->operation();
delete product;
product = NULL;
return 0;
}
3.观察者模式
接口
Subject(目标)
——目标知道它的观察者。可以有任意多个观察者观察同一个目标;
——提供注册和删除观察者对象的接口。
Observer(观察者)
——为那些在目标发生改变时需获得通知的对象定义一个更新接口。
实现类
ConcreteSubject(具体目标)
——将有关状态存入各ConcreteObserver对象;
——当它的状态发生改变时,向它的各个观察者发出通知。
ConcreteObserver(具体观察者)
——维护一个指向ConcreteSubject对象的引用;
——存储有关状态,这些状态应与目标的状态保持一致;
——实现Observer的更新接口以使自身状态与目标的状态保持一致。
接口:
class Observer
{
public:
virtual void Update(int) = 0;
};
class Subject
{
public:
virtual void Attach(Observer *) = 0;
virtual void Detach(Observer *) = 0;
virtual void Notify() = 0;
};
实现类:
class ConcreteObserver : public Observer
{
public:
ConcreteObserver(Subject *pSubject) : m_pSubject(pSubject){}
void Update(int value)
{
cout << "ConcreteObserver get the update. New State:" << value << endl;
}
private:
Subject *m_pSubject;
};
class ConcreteObserver2 : public Observer
{
public:
ConcreteObserver2(Subject *pSubject) : m_pSubject(pSubject){}
void Update(int value)
{
cout << "ConcreteObserver2 get the update. New State:" << value << endl;
}
private:
Subject *m_pSubject;
};
class ConcreteSubject : public Subject
{
public:
void Attach(Observer *pObserver);
void Detach(Observer *pObserver);
void Notify();
void SetState(int state)
{
m_iState = state;
}
private:
std::list<Observer *> m_ObserverList;
int m_iState;
};
void ConcreteSubject::Attach(Observer *pObserver)
{
m_ObserverList.push_back(pObserver);
}
void ConcreteSubject::Detach(Observer *pObserver)
{
m_ObserverList.remove(pObserver);
}
void ConcreteSubject::Notify()
{
std::list<Observer *>::iterator it = m_ObserverList.begin();
while (it != m_ObserverList.end())
{
(*it)->Update(m_iState);
++it;
}
}
main:
int main()
{
// Create Subject
ConcreteSubject *pSubject = new ConcreteSubject();
// Create Observer
Observer *pObserver = new ConcreteObserver(pSubject);
Observer *pObserver2 = new ConcreteObserver2(pSubject);
// Change the state
pSubject->SetState(2);
// Register the observer
pSubject->Attach(pObserver);
pSubject->Attach(pObserver2);
pSubject->Notify();
// Unregister the observer
pSubject->Detach(pObserver);
pSubject->SetState(3);
pSubject->Notify();
delete pObserver;
delete pObserver2;
delete pSubject;
}
乱七八糟的cpp知识合集
(一)#pragma pack(n)
默认的语法是:按照结构体中最大的size对齐
对结构体等添加程序员自定义的对齐规则。
struct Test{
int i;
double j;
char c;
};
size=24(double=8,8*3=24)
如果按照#pragma pack(1),则按照1对齐
#pragma pack(1)
struct Test{
int i;
double j;
char c;
};
size=13(int=4,doube=8,char=1)
(二)关于函数
① 重载(overloading)
同名函数,不同参数。(不能以同名函数,不同返回值的方式)
void Print(int i)
{
cout << i << endl;
//console out 控制台输出
}
void Print(char * str)
{
cout << str << endl;
}
int main(int argc, char* argv[])
{
Print(1);
Print("Hello");
}
② 默认参数(default parameter)
规则:
- 默认的参数必须放后面;
- 头文件与源文件成对出现;
- 应用的时候,如果有默认参数,一概不传递。
void fun(int a,int b=10,int c=20);
//如果没有传参结果是默认值,如果传了则是传了的值。
③ 占位符
占位参数,一般用于overloading:凑同名函数不同参数;
传啥都行>>MUST BE ZERO
void fun(int)
{
}
int main(int argc, char* argv[])
{
fun(1);
return 0;
}
(三)成员变量的初始化方法
- 普通私有成员变量:类构造器初始(可初始值,可赋值)
- const私有成员变量:
Test::Test(void):var1(11111),var2(22222){}
类构造器初始,only初始值不能省- static私有成员变量:
int Test::var3 = 3333333;
自己定义构造- static const私有成员变量:
static const int var4=4444;
-----------------Test.h----------------------------
#pragma once
class Test
{
private :
int var1;
// int var11= 4; 错误的初始化方法
const int var2 ;
// const int var22 =22222; 错误的初始化方法
static int var3;
// static int var3333=33333; 错误,只有静态常量int成员才能直接赋值来初始化
static const int var4=4444; //正确,静态常量成员可以直接初始化
static const int var44;
public:
Test(void);
~Test(void);
};
--------------------Test.cpp-----------------------------------
#include ".\test.h"
int Test::var3 = 3333333; //静态成员的正确的初始化方法
// int Test::var1 = 11111;; 错误静态成员才能初始化
// int Test::var2 = 22222; 错误
// int Test::var44 = 44444; // 错误的方法,提示重定义
Test::Test(void):var1(11111),var2(22222)正确的初始化方法//var3(33333)不能在这里初始化
{
var1 =11111; //正确, 普通变量也可以在这里初始化
//var2 = 222222; 错误,因为常量不能赋值,只能在 "constructor initializer (构造函数的初始化列表)" 那里初始化
var3 =44444; //这个赋值是正确的,不过因为所有对象一个静态成员,所以会影响到其他的,这不能叫做初始化了吧
}
Test::~Test(void){}
(四)c补充知识
①char* 和 char[]
- char [],表示的是一个char类型的数组指针,该指针所指向的数组内容是保存在栈上面的,是可以修改的。
- char *是一个字符串指针,这个指针指向的是字符串第一个字符的地址,而这个指针存在栈上,但是字符串的内容并不在栈里面,而在字符常量区域里面储存。
- 查看char *str1 = "abcd1234"与char *str2 = "abcd1234"的地址时,他们都是储存的字符a的地址,所以这个地址时相同的,其 %p 的值也是一样的。
- 但是char str3[] = “abcd1234” 与 char str[] = "abcd1234"是分别两个char类型的数组,而str3与str4分别表示的是char型的数组指针,所以他们的地址时不同的。