I have this code (question from exam) and there are many errors in the code, here is the code:
class A
{
int z;
static int y;
public:
A() {(*this).incrementY();}
int x () { y=y+2; return z;}
static int getY(){ return z;}
void incrementY() {y++}
};
class B: A
{
public:
B (): A() {(*this).incrementY();};
};
class C : public B
{
public:
C(): B() {(*this).incrementY;() }
};
int main()
{
A::x();
A a;
a.getY();
A::getY();
B b;
C c;
C::getY();
b.x();
}
There is a private inheritance. This means that all the methods and variables in B become private to any class that inherits from B?
There are so many that you cannot but get tired. Here are some:
void incrementY() {y++}
No ; after y++.
24. A::x();
Non static member cannot be invoked via a class name.
25. A a;
No definition of static member y. If y was a const this would've been okay. This is a bit tricky so I'll quote.
9.4.2 Static data members
The declaration of a static data member in its class definition is not a definition and may be of an incomplete type other than cv-qualified void [...]
If a static data member is of const effective literal type, its declaration in the class definition can specify
a constant-initializer brace-or-equal-initializer with an initializer-clause that is an integral constant expression.
A static data member of effective literal type can be declared in the class definition with the
constexpr specifier; if so, its declaration shall specify a constant-initializer brace-or-equal-initializer with an initializer-clause that is an integral constant expression. In both these cases, the member may appear in integral constant expressions. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.
26. a.getY();
27. A::getY();
Illegal reference to non-static member A::z.
Taken care of by first observation.
28. B b;
29. C c;
30. C::getY();
getY() is a private member of B, not visible to C, let alone be public.
31. b.x();
The member function x() inherited from A is private.
You are correct there (although it actually means that all A's methods become inaccessible).
However, there are a few other problems:
A::x() // will not work as x is not static.
a.getY() // will not work as getY() is static.
C::getY() // Cannot access getY()
Yes, that is correct, although you could just compile it with any number of online C++ compilers to verify.
Related
Consider the following example:
class A { int x; };
Now what is A::x?
It cannot be an lvalue because it does not refer to a storage Location.
It cannot be a type, because the type would be decltype(A::x).
It is, in fact, an lvalue. [expr.prim.id.qual]/2:
A nested-name-specifier that denotes a class [...] followed by the
name of a member of either that class ([class.mem]) or one of its base
classes, is a qualified-id [...]. The result is the member. The type
of the result is the type of the member. The result is an lvalue if
the member is a static member function or a data member and a prvalue
otherwise.
Though its usage outside a class member access expression is severely restricted by [expr.prim.id]/2, it can notably be used in unevaluated operands, where its lvalueness can manifest:
struct A {
int x;
};
void f(int &);
using p = decltype(&(A::x)); // p is int*; the parens prevents forming a pointer-to-member
using q = decltype(f(A::x)); // q is void
A::x is merely a less ambiguous way of referring to the member x which can be necessary if the local context shadows that member:
Example 1:
A child class also has a member x.
struct B : public A
{
int x;
void foo()
{
int local_x = x;
int parent_x = A::x;
}
}
which would compile had you made x protected in class A (it's currently private).
Example 2:
You can even use it in a member function of A that has x as a parameter:
class A {
int x;
void foo(int x)
{
int parameter_x = x;
int member_x = A::x;
}
};
In addition to Bathsheba's answer, which is correct when you're within the class's scope, A::x can also be used as part of a pointer-to-member expression, i.e. &A::x, which returns a int A::*.
In this case, the standalone A::x is not valid and therefore the question of what it returns is moot.
From the C++ Standard (5.1 Primary expressions)
9 A nested-name-specifier that denotes a class, optionally followed by
the keyword template (14.2), and then followed by the name of a member
of either that class (9.2) or one of its base classes (Clause 10), is
a qualified-id; 3.4.3.1 describes name lookup for class members that
appear in qualified-ids. The result is the member. The type of the
result is the type of the member. The result is an lvalue if the
member is a static member function or a data member and a prvalue
otherwise. [ Note: a class member can be referred to using a
qualified-id at any point in its potential scope (3.3.7). —end note
]...
The qualified name allows to specify an otherwise hidden name for example when derived and base classes have members with the same name. For example
#include <iostream>
struct A
{
int i;
};
struct B : A
{
B( int i ) : A { i }, i( 2 * A::i ) {}
int i;
std::ostream &out( std::ostream &os = std::cout ) const
{
return os << "A::i = " << A::i << ", B::i = " << B::i;
}
};
int main()
{
B( 10 ).out() << std::endl;
return 0;
}
The program output is
A::i = 10, B::i = 20
Also (5.1 Primary expressions)
13 An id-expression that denotes a non-static data member or
non-static member function of a class can only be used:
(13.1) — as part of a class member access (5.2.5) in which the object
expression refers to the member’s class63 or a class derived from that
class, or
(13.2) — to form a pointer to member (5.3.1), or
(13.3) — if that id-expression denotes a non-static data member and it
appears in an unevaluated operand.
A::x is exact equivalent of accessing static variable in JAVA.
Regarding your example:-
1) x will be variable for distinct objects
2) A::x will be the variable for the class itself. Regardless of how many objects of A has been declared for every object the value of A::x will be same until assignment.
For example:-
#include<iostream>
using namespace std;
class A {
public:
static int x;
};
int A::x;
int main()
{
A a,b;
a.x=8;
b.x=6;
A::x=10;
return 0;
}
I'm reading Scott Meyers' C++ and come across this example:
class GamePlayer{
private:
static const int NumTurns = 5;
int scores[NumTurns];
// ...
};
What you see above is a declaration for NumTurns, not a definition.
Why not a definition? It looks like we initialize the static data member with 5.
I just don't understand what it means to declare but not define a variable with the value 5. We can take the address of the variable fine.
class A
{
public:
void foo(){ const int * p = &a; }
private:
static const int a = 1;
};
int main ()
{
A a;
a.foo();
}
DEMO
Because it isn't a definition. Static data members must be defined outside the class definition.
[class.static.data] / 2
The declaration of a static data member in its class definition is not
a definition and may be of an incomplete type other than cv-qualified
void. The definition for a static data member shall appear in a
namespace scope enclosing the member’s class definition.
As for taking the address of your static member without actually defining it, it will compile, but it shouldn't link.
you need to put a definition of NumTurns in source file, like
const int GamePlayer::NumTurns;
Please consider the following mini example
// CFoo.hpp
class CFoo{
private:
static const double VPI = 0.5;
public:
double getVpi();
};
// CFoo.cpp
#include "CFoo.hpp"
double CFoo::getVpi(){
double x = -VPI;
return x;
}
// main.cpp
#include "CFoo.hpp"
int main(){
CFoo aFoo();
return 0;
}
Lining the program with gcc version 4.5.1 produces the error CFoo.cpp: undefined reference to CFoo::VPI. The error dose not occur if
VPI is not negated
the negation is written as double x = -1 * VPI;
Declaration and definition of class CFoo happen in the same file
Do you know the reason for this error?
There are multiple problems with your code. Primarily, this is not valid C++03:
class CFoo{
private:
static const double VPI = 0.5;
// ...
};
The declaration of a static data member can specify a constant initializer if and only if that initializer is const integral or const enumeration type. 0.5 is neither of these, and hence your code is not valid C++. 9.4.2 Static data members covers this:
2/ The declaration of a static data member in its class definition is
not a definition [...]The definition for a static data member shall
appear in a namespace scope enclosing the member’s class definition.
[...]
4/ If a static data member is of const integral or const enumeration
type, its declaration in the class definition can specify a
constant-initializer which shall be an integral constant expression
(5.19).
In order to initialize VPI, you must do so in the CPP file:
header:
class CFoo{
private:
static const double VPI;
};
cpp :
const double CFoo::VPI = 0.5;
Another problem, unrelated, is here:
int main(){
CFoo aFoo(); // NOT OK
return 0;
The expression CFoo aFoo(); doesn't do what you think it does. You think it declares an object aFoo of type CFoo and initializes it using CFoo's default constructor. But what it actually does is declare a function named aFoo taking no parameters, returning a CFoo by value. This is known as the most vexing parse. In order to do what you want, simple omit the parenthesis:
CFoo aFoo;
Should we define a static const member outside of the class definition even if it is initialised inside the class?
#include<iostream>
using namespace std;
class abc
{
static const int period=5;
int arr[period];
public:
void display()
{
cout<<period<<endl;
}
};
const int abc::period;
int main()
{
abc a;
a.display();
return 0;
}
After commenting // const int abc::period;, both versions of the code run fine on gcc 4.3.4. So I want to ask why do both versions work and which one is standard compliant?
You are defining the static memberperiod by writing const int abc::period;. You are allowed to provide an in class initializer for static const member of a class but that's not definition, but that's merely a declaration.
9.4.2/4 - If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer.
Your code compiles even without the definition because you are not taking the address of the static member. Bjarne Stroustrup mentions in the C++-FAQ here that You can take the address of a static member if (and only if) it has an out-of-class definition
Is it possible to initialize a static constant member in a class definition? Please see below for the code,
class foo
{
public:
foo(int p) : m_p(p){}
~foo(){}
private:
int m_p;
};
class bar
{
public:
bar(){}
~bar(){}
public:
static const foo m_foo = foo( 2 ); //is this possible?
};
Many thanks.
Short answer:
No, until the static member is const and is of integral or enumeration type.
Long answer:
$9.4.2/4 - "If a static data member is
of const integral or const enumeration
type, its declaration in the class
definition can specify a
constant-initializer which shall be an
integral constant expression (5.19).
In that case, the member can appear in
integral constant expressions. The
member shall still be defined in a
namespace scope if it is used in the
program and the namespace scope
definition shall not contain an
initializer."
Not for a static data member of class type, as in your example.
9.4.2/2:
The declaration of a static data
member in its class definition is not
a definition ... The definition for
a static data member shall appear in a
namespace scope enclosing the member’s
class definition.
9.4.2/4:
If a static data member is of const
integral or const enumeration type,
its declaration in the class
definition can specify a
constant-initializer which shall be an
integral constant expression (5.19).
In that case, the member can appear in
integral constant expressions. The
member shall still be defined in a
name- space scope if it is used in the
program and the namespace scope
definition shall not contain an
initializer.